When should I use a DataReader vs a DataSet?
Use a DataReader for fast, forward only reads. Use a DataSet when you need caching, relations, or offline edits.
ADO.NET is the data access stack for .NET. It gives you a clean way to connect to a database, run commands, and move data between tiers. You work with two main styles. Streaming with a DataReader for fast, forward only reads. Disconnected with a DataSet for cached data and updates.
A DataSet is an in memory view of one or more tables. It can hold relations, constraints, and views. You fill it with a DataAdapter and you can save it as XML when needed.
using System.Data;
using System.Data.SqlClient;
var cs = "Server=.;Database=Northwind;Integrated Security=true";
using var conn = new SqlConnection(cs);
using var da = new SqlDataAdapter("SELECT Id, Name FROM dbo.Categories", conn);
// Build insert/update/delete commands automatically for demo use
using var cb = new SqlCommandBuilder(da);
var ds = new DataSet();
da.Fill(ds, "Categories");
// Edit in memory
var table = ds.Tables["Categories"];
table.Rows[0]["Name"] = "Beverages";
// Send only changed rows
var changed = ds.GetChanges();
if (changed != null)
{
da.Update(changed, "Categories");
ds.Merge(changed);
}
Inside a DataSet you get DataTable objects. A table has a DataColumnCollection that defines the schema. Relations between tables are handled by DataRelation and can enforce unique and foreign key rules.
var ds = new DataSet();
var customers = new DataTable("Customers");
customers.Columns.Add("CustomerId", typeof(int));
customers.Columns.Add("Name", typeof(string));
customers.PrimaryKey = new[] { customers.Columns["CustomerId"] };
var orders = new DataTable("Orders");
orders.Columns.Add("OrderId", typeof(int));
orders.Columns.Add("CustomerId", typeof(int));
ds.Tables.Add(customers);
ds.Tables.Add(orders);
// Enforce parent child rules
var rel = new DataRelation("FK_Customer_Orders",
customers.Columns["CustomerId"], orders.Columns["CustomerId"], true);
ds.Relations.Add(rel);
Use DataView to sort or filter a table for display. Use DataReader when you just need to stream rows quickly. The reader is forward only and read only which makes it very fast.
using var conn = new SqlConnection(cs);
using var cmd = new SqlCommand("SELECT TOP 5 Id, Name FROM dbo.Categories", conn);
conn.Open();
using var rdr = cmd.ExecuteReader();
while (rdr.Read())
{
Console.WriteLine($"{rdr.GetInt32(0)}: {rdr.GetString(1)}");
}
A Connection holds the link to the database. Commands run SQL or stored procedures. You can start a transaction from a connection when a change spans multiple statements.
using var conn = new SqlConnection(cs);
conn.Open();
using var tx = conn.BeginTransaction();
try
{
var insert = new SqlCommand(
"INSERT INTO dbo.Categories(Name) VALUES(@n)", conn, tx);
insert.Parameters.AddWithValue("@n", "New Category");
insert.ExecuteNonQuery();
var update = new SqlCommand(
"UPDATE dbo.Categories SET Name=@n WHERE Id=@id", conn, tx);
update.Parameters.AddWithValue("@n", "Updated");
update.Parameters.AddWithValue("@id", 1);
update.ExecuteNonQuery();
tx.Commit();
}
catch
{
tx.Rollback();
throw;
}
Providers sit between your code and the data source. SqlClient talks to SQL Server. Odbc works with ODBC sources. OleDb targets OLE DB providers. OracleClient targets Oracle in older apps. XML support lives under System.Xml.
using System.Data.Common;
// Create provider neutral code
string provider = "System.Data.SqlClient"; // or System.Data.Odbc, System.Data.OleDb
DbProviderFactory f = DbProviderFactories.GetFactory(provider);
using var conn = f.CreateConnection();
conn.ConnectionString = "...";
conn.Open();
ADO.NET works well with XML. You can read and write XML from a DataSet, and load or save the schema as XSD.
var ds = new DataSet();
ds.ReadXml("orders.xml"); // load data
ds.WriteXml("orders-out.xml"); // save data
ds.WriteXmlSchema("orders.xsd"); // save schema
In multi tier apps, send only the changed rows. Call GetChanges on the DataSet to collect edits, then push them with a DataAdapter. When the server returns new values, merge them back into the original DataSet with Merge.
Open a connection late and close it early. Use pooling. Prefer a DataReader for simple lists. Use a DataSet when you need caching, relations, or offline edits. Handle concurrency with timestamps or row versions.
Use a DataReader for fast, forward only reads. Use a DataSet when you need caching, relations, or offline edits.
Collect edited rows with GetChanges, update with a DataAdapter, then merge server values with Merge.
Yes when a set of statements must succeed as one unit. Begin a transaction, attach it to commands, then commit or roll back.
A DataSet can read and write XML and XSD which makes it useful for exchange and batch work.