DataSet vs DataTable in ADO.NET
A DataTable is one in-memory table: columns, rows, constraints. A DataSet is a container of DataTables plus the relations between them. Both are disconnected: once filled, they no longer need the database connection.
Side by side
| DataTable | DataSet | |
|---|---|---|
| Holds | One table | A collection of tables (Tables) and Relations |
| Typical fill | adapter.Fill(table) or table.Load(reader) | adapter.Fill(dataSet), one table per result set |
| Relations | No | Yes, with parent/child navigation and cascading constraints |
| XML | WriteXml/ReadXml (needs a table name) | WriteXml/ReadXml with schema |
| Overhead | Lower | Higher |
Filling a DataTable
var customers = new DataTable("Customers");
using (var con = new SqlConnection(cs))
using (var da = new SqlDataAdapter("SELECT CustomerId, Name, Country FROM dbo.Customers", con))
{
da.Fill(customers); // opens and closes the connection itself
}
foreach (DataRow row in customers.Rows)
{
string name = row.Field<string>("Name"); // System.Data.DataSetExtensions
}
A DataSet with a relation
var ds = new DataSet();
using (var con = new SqlConnection(cs))
using (var cmd = new SqlCommand(
"SELECT CustomerId, Name FROM dbo.Customers; SELECT OrderId, CustomerId, Total FROM dbo.Orders;", con))
using (var da = new SqlDataAdapter(cmd))
{
da.TableMappings.Add("Table", "Customers");
da.TableMappings.Add("Table1", "Orders");
da.Fill(ds);
}
ds.Relations.Add("CustomerOrders",
ds.Tables["Customers"].Columns["CustomerId"],
ds.Tables["Orders"].Columns["CustomerId"]);
foreach (DataRow c in ds.Tables["Customers"].Rows)
{
DataRow[] orders = c.GetChildRows("CustomerOrders");
}
Without table mappings, the tables are named Table, Table1 and so on, in result-set order. Adding the relation also creates a foreign-key constraint: an order whose customer is missing makes Relations.Add throw.
When to use which
- DataTable for one result to bind to a grid, export or pass around.
- DataSet when several related tables travel together, when you use a typed DataSet designer, or when you need XML with schema.
- Neither when you just display rows once: a data reader is lighter (see DataSet, DataReader, DataAdapter and DataView). In new code, a
List<T>of your own classes is usually clearer.
Both are heavy in session state and view state; store keys rather than whole tables where you can.
Typed DataSets and alternatives
Visual Studio's DataSet designer generates typed DataSets with a class per table and properties per column, plus TableAdapters that hold the SQL. They remove string column names from code and are common in Web Forms projects from the 2005–2012 era. They are still supported on .NET Framework but receive no new features. When maintaining such a project, keep using them consistently; when writing new data access, plain classes with ADO.NET, Dapper or Entity Framework are easier to test and to move to modern .NET.
Official documentation
- Microsoft Learn: DataSets, DataTables and DataViews
- Microsoft Learn: DataSet class, DataTable class, DataRelation