DataTable中的dt.Load()方法
using (SqlDataReader Dr = sqlcomm.ExecuteReader())
{
// 把 Reader 的 Schema 加载到 DataTable(自动创建列)
dt.Load(Dr);
}
dt.Load(Dr); 这句代码到底干了什么? 它相当于直接把"传送带"的出口,插进了"空仓库"的门里! DataTable 极其聪明,当它执行 Load() 方法时,它会自动完成两步神操作:
-
先看图纸建货架(加载 Schema) :它会先看一眼传送带送来的"货物规格说明书(数据库表的列名、数据类型)",然后瞬间在自己的内存里自动建好一模一样的列(Columns) 。你完全不需要像之前那样手动去
dt.Columns.Add(...)。 -
全自动卸货(填充 Rows) :建好列之后,它会自动让传送带 (
Dr.Read()) 疯狂转动,把所有传送过来的数据一行不落地装进自己的Rows集合里,直到传送带上没货为止。
"既然有 SqlDataAdapter.Fill(dt) 这么爽的全自动包工头,为什么还要用 SqlDataReader + dt.Load() 这种写法呢?"
这叫"没有中间商赚差价"!
-
SqlDataAdapter.Fill()(包工头模式) : 你把命令交给包工头,包工头在底层其实也是去偷偷创建了一个SqlCommand,又偷偷创建了一个SqlDataReader,最后偷偷把数据塞进表里。它干活很全面,但因为封装得太厚,内存和性能开销相对大一点点。 -
dt.Load(Dr)(极客直连模式) : 你省去了创建SqlDataAdapter对象的开销,直接操控底层最快的读取器 (SqlDataReader)。这种写法在处理中大型数据查询 时,执行速度会比DataAdapter更快,内存占用也更干净!
三、 唯一的高频"翻车坑点"(极其重要!)
既然你辞退了全自动的"包工头",决定亲自指挥,那你就必须承担起所有的责任!
包工头 SqlDataAdapter 最大的好处是会自动帮你打开和关闭 SqlConnection 连接 。 但是在你这套组合技里,不会有人帮你关连接!
C#
DataTable dt = new DataTable();
// 第一层防弹衣:保护网络连接
using (SqlConnection conn = new SqlConnection(connStr))
{
// 必须要手动打开连接!
conn.Open();
// 第二层防弹衣:保护卡车指令
using (SqlCommand sqlcomm = new SqlCommand("SELECT * FROM Users WHERE Age > @Age", conn))
{
sqlcomm.Parameters.Add("@Age", SqlDbType.Int).Value = 18;
// 第三层防弹衣:保护传送带(就是你发给我的这段)
using (SqlDataReader Dr = sqlcomm.ExecuteReader())
{
// 完美卸货!自动建表头,自动填数据!
dt.Load(Dr);
} // 离开大括号,Dr 传送带安全拆除
} // 离开大括号,sqlcomm 卡车安全销毁
} // 离开大括号,conn 物理连接安全还给连接池!
// 此时断网了,但数据已经全在 dt 里了,你可以把它拿去绑定 WPF 界面了!
return dt;
// 5. 🌟 祭出作弊神器:SqlCommandBuilder
// 只要你给了 SelectCommand,它能根据 Select 自动逆向生成 Insert/Update/Delete 语句!
SqlCommandBuilder builder = new SqlCommandBuilder(adapter);
using System.Data;
using System.Data.SqlClient;
public void OfflineDataSyncDemo()
{
string connStr = "Server=.;Database=MyCorpDB;Integrated Security=True;";
// 1. 只写一句查询 SQL(作为基础图纸)
string selectSql = "SELECT UserId, UserName, Age FROM Users";
using (SqlConnection conn = new SqlConnection(connStr))
{
// 2. 召唤桥接器(物流大队长)!直接把图纸和路交给他
SqlDataAdapter adapter = new SqlDataAdapter(selectSql, conn);
// 3. 在本地内存里建一个空仓库
DataTable dt = new DataTable();
// 4. 【绝招一:拉货】全自动拉取,根本不需要写 conn.Open() !
adapter.Fill(dt);
// ========= 此时,网络已经断开了!你可以随便玩内存里的 dt =========
// 假设我们在内存里改了第一行用户的名字 (模拟在界面上修改)
dt.Rows[0]["UserName"] = "被修改的新名字";
// 假设我们在内存里新增了一个用户
DataRow newRow = dt.NewRow();
newRow["UserId"] = 999;
newRow["UserName"] = "新来的萌新";
newRow["Age"] = 18;
dt.Rows.Add(newRow);
// ========= 玩够了,准备同步回数据库 =========
// 5. 🌟 祭出作弊神器:SqlCommandBuilder
// 只要你给了 SelectCommand,它能根据 Select 自动逆向生成 Insert/Update/Delete 语句!
SqlCommandBuilder builder = new SqlCommandBuilder(adapter);
try
{
// 6. 【绝招二:同步】一键把刚才所有的增删改操作,全部推回真实数据库!
int changedCount = adapter.Update(dt);
Console.WriteLine($"太帅了!成功同步了 {changedCount} 条改动到数据库!");
}
catch(Exception ex)
{
Console.WriteLine("同步失败,可能是并发冲突:" + ex.Message);
}
}
}