c# DataSet 类

在 C# 的ADO.NET体系中,DataSet是一个内存中的数据容器,可视为 "离线数据库",用于存储和管理关系型数据(包含表、表关系、约束等),且独立于底层数据源(如 SQL Server、MySQL 等)。它是处理复杂数据关系、离线数据操作的核心组件。

一、DataSet 的核心特性

  1. 离线数据存储:数据加载后与数据源断开连接,仍可在内存中操作,适合客户端离线场景。

  2. 关系型结构 :可包含多个DataTable(数据表),表之间通过DataRelation(关系)关联,支持主外键逻辑。

  3. 数据状态跟踪:自动记录数据行的修改状态(新增、修改、删除),便于批量同步回数据源。

  4. 架构独立性 :自带数据架构(Schema),记录字段类型、约束(主键、唯一键等),不依赖数据源元数据。

二、DataSet 的基本结构

DataSet的结构类似小型数据库,核心组成如下:

  • Tables集合 :存储多个DataTable对象(数据表),每个DataTable对应一组结构化数据。

  • Relations集合 :存储DataRelation对象,定义DataTable之间的关联关系(如 "客户 - 订单" 的一对多关系)。

  • ExtendedProperties:存储自定义键值对信息(如数据集描述、创建时间等)。

三、DataSet 的常用方法与属性

方法 / 属性 说明
Tables 获取DataTableCollection,用于访问或管理包含的DataTable
Relations 获取DataRelationCollection,用于管理表之间的关系
DataSetName 获取或设置数据集名称
AcceptChanges() 提交所有数据修改(将RowState重置为Unchanged
RejectChanges() 撤销所有未提交的修改(恢复到上次AcceptChanges后的状态)
Merge(DataSet) 将另一个DataSet的数据合并到当前数据集(常用于合并离线修改)
Clear() 清空所有表中的数据(保留表结构)
GetXml() 将数据集内容转换为 XML 字符串
ReadXml(string) 从 XML 文件加载数据到数据集

四、使用示例:完整操作流程

以下通过 "客户 - 订单" 数据模型,展示DataSet的创建、数据操作、关系管理和持久化的完整流程。

1. 创建 DataSet 并定义表结构
复制代码
// 1. 创建DataSet
DataSet salesDataSet = new DataSet("SalesData"); // 数据集名称
​
// 2. 创建客户表(Customers)
DataTable customerTable = new DataTable("Customers");
// 定义列(含主键)
customerTable.Columns.Add("CustomerId", typeof(int)); // 主键列
customerTable.Columns.Add("Name", typeof(string));
customerTable.Columns.Add("Phone", typeof(string));
// 设置主键
customerTable.PrimaryKey = new[] { customerTable.Columns["CustomerId"] };
​
// 3. 创建订单表(Orders)
DataTable orderTable = new DataTable("Orders");
orderTable.Columns.Add("OrderId", typeof(int)); // 主键列
orderTable.Columns.Add("CustomerId", typeof(int)); // 外键(关联Customers)
orderTable.Columns.Add("Amount", typeof(decimal));
orderTable.Columns.Add("OrderDate", typeof(DateTime));
orderTable.PrimaryKey = new[] { orderTable.Columns["OrderId"] };
​
// 4. 将表添加到DataSet
salesDataSet.Tables.Add(customerTable);
salesDataSet.Tables.Add(orderTable);
​
// 验证结构
Console.WriteLine($"数据集名称:{salesDataSet.DataSetName}");
Console.WriteLine($"包含表数量:{salesDataSet.Tables.Count}"); // 输出:2
2. 填充数据与操作行
复制代码
// 1. 向客户表添加数据
DataTable customers = salesDataSet.Tables["Customers"];
customers.Rows.Add(1, "张三", "13800138000"); // 直接传值(按列顺序)
customers.Rows.Add(2, "李四", "13900139000");
​
// 2. 向订单表添加数据
DataTable orders = salesDataSet.Tables["Orders"];
orders.Rows.Add(1001, 1, 999.99m, new DateTime(2024, 1, 15)); // 张三的订单
orders.Rows.Add(1002, 1, 1599.50m, new DateTime(2024, 3, 20)); // 张三的订单
orders.Rows.Add(1003, 2, 599.00m, new DateTime(2024, 5, 10)); // 李四的订单
​
// 3. 修改数据
DataRow zhangsanRow = customers.Rows.Find(1); // 通过主键查找
zhangsanRow["Phone"] = "13812345678"; // 修改手机号
​
// 4. 删除数据
DataRow orderToDelete = orders.Rows.Find(1003); // 查找订单1003
if (orderToDelete != null)
    orderToDelete.Delete(); // 标记删除(未真正删除,需AcceptChanges确认)
​
// 5. 查看行状态(跟踪修改)
foreach (DataRow row in customers.Rows)
{
    Console.WriteLine($"客户ID: {row["CustomerId"]}, 状态: {row.RowState}");
}
// 输出:
// 客户ID: 1, 状态: Modified(已修改)
// 客户ID: 2, 状态: Unchanged(未修改)
3. 定义表关系与数据导航

通过DataRelation建立表之间的关联,实现父子数据的快速导航。

复制代码
// 1. 创建“客户-订单”关系(一对多)
DataRelation customerOrderRel = new DataRelation(
    "FK_Customer_Order", // 关系名称
    salesDataSet.Tables["Customers"].Columns["CustomerId"], // 父表主键
    salesDataSet.Tables["Orders"].Columns["CustomerId"] // 子表外键
);
// 添加关系到DataSet
salesDataSet.Relations.Add(customerOrderRel);
​
// 2. 通过父行查找子行(查询张三的所有订单)
DataRow zhangsan = customers.Rows.Find(1);
if (zhangsan != null)
{
    // 获取张三的所有订单(子行)
    DataRow[] zhangsanOrders = zhangsan.GetChildRows(customerOrderRel);
    Console.WriteLine($"客户 {zhangsan["Name"]} 的订单数量:{zhangsanOrders.Length}");
    
    foreach (DataRow order in zhangsanOrders)
    {
        Console.WriteLine($"订单ID:{order["OrderId"]},金额:{order["Amount"]}");
    }
}
​
// 3. 通过子行查找父行(查询订单1001的客户)
DataRow order1001 = orders.Rows.Find(1001);
if (order1001 != null)
{
    DataRow customer = order1001.GetParentRow(customerOrderRel);
    Console.WriteLine($"订单1001的客户:{customer["Name"]}"); // 输出:张三
}
4. 数据持久化(XML 导入 / 导出)

DataSet支持直接与 XML 互转,方便数据持久化或传输。

复制代码
// 1. 将数据导出为XML(含结构和数据)
string xmlData = salesDataSet.GetXml();
Console.WriteLine("XML数据:");
Console.WriteLine(xmlData);
​
// 2. 保存到XML文件
salesDataSet.WriteXml("SalesData.xml"); // 仅数据
salesDataSet.WriteXmlSchema("SalesDataSchema.xml"); // 仅结构(Schema)
​
// 3. 从XML文件加载数据
DataSet newDataSet = new DataSet();
newDataSet.ReadXml("SalesData.xml");
newDataSet.ReadXmlSchema("SalesDataSchema.xml"); // 加载结构确保类型正确
Console.WriteLine($"从XML加载的客户表行数:{newDataSet.Tables["Customers"].Rows.Count}");
5. 与数据库同步(结合 DataAdapter)

DataSet的修改可通过DataAdapter批量更新回数据源,利用其状态跟踪功能自动生成增删改命令。

复制代码
string connectionString = "Server=.;Database=SalesDB;Integrated Security=True;";
​
// 1. 创建DataAdapter(作为DataSet与数据库的桥梁)
using (SqlDataAdapter adapter = new SqlDataAdapter())
{
    // 设置查询命令(用于从数据库加载数据)
    adapter.SelectCommand = new SqlCommand(
        "SELECT CustomerId, Name, Phone FROM Customers", 
        new SqlConnection(connectionString)
    );
​
    // 自动生成增删改命令(需引用System.Data.SqlClient)
    SqlCommandBuilder cmdBuilder = new SqlCommandBuilder(adapter);
​
    // 2. 从数据库填充DataSet
    DataSet dbDataSet = new DataSet();
    adapter.Fill(dbDataSet, "Customers"); // 填充到"Customers"表
​
    // 3. 在内存中修改数据(模拟用户操作)
    DataTable dbCustomers = dbDataSet.Tables["Customers"];
    dbCustomers.Rows[0]["Phone"] = "13888888888"; // 修改
    dbCustomers.Rows.Add(3, "王五", "13700137000"); // 新增
    dbCustomers.Rows[1].Delete(); // 删除
​
    // 4. 将修改同步回数据库(根据RowState自动执行对应SQL)
    int updatedRows = adapter.Update(dbDataSet, "Customers");
    Console.WriteLine($"成功同步 {updatedRows} 条修改到数据库");
}

五、DataSet 的适用场景与局限性

适用场景:
  • 离线数据处理:客户端需在无网络环境下操作数据(如移动应用)。

  • 复杂数据关系:需维护多表关联(如订单 - 订单明细 - 客户),并频繁导航数据。

  • 批量数据更新:需一次性提交多条增删改操作,减少数据库交互次数。

  • 数据缓存:重复使用的数据集可缓存到内存,提升性能。

局限性:
  • 内存占用高:数据全部加载到内存,不适合处理百万级以上大数据。

  • 数据一致性风险:离线操作可能导致与数据源数据冲突(需额外处理并发)。

  • 性能开销 :相比DataReader(流式读取),初始化和序列化DataSet的开销更大。

六、总结

DataSet是 C# 中处理关系型数据的强大工具,其核心价值在于离线数据管理复杂关系维护 。通过与DataTableDataRelationDataAdapter等类配合,可构建完整的内存数据模型,灵活支持从数据加载、修改到同步的全流程。在实际开发中,需根据数据量和业务场景选择使用(小批量、多关系数据优先,超大数据量建议用DataReader)。

相关推荐
想用offer打牌2 小时前
数据库大事务有什么危害(面试版)
数据库·后端·架构
廋到被风吹走2 小时前
【数据库】【Redis】数据结构全景图:命令、场景与避坑指南
数据结构·数据库·redis
Jaising6662 小时前
Spring 错误使用事务导致数据可见性问题分析
数据库·spring boot
xixingzhe22 小时前
数据、数据库分类
数据库
松涛和鸣2 小时前
34、 Linux IPC进程间通信:无名管道(Pipe) 和有名管道(FIFO)
linux·服务器·c语言·网络·数据结构·数据库
秦苒&2 小时前
【C语言】详解数据类型和变量(一):数据类型介绍、 signed和unsigned、数据类型的取值范围、变量、强制类型转换
c语言·开发语言·c++·c#
云老大TG:@yunlaoda3602 小时前
如何使用华为云国际站代理商的FunctionGraph进行事件驱动的应用开发?
大数据·数据库·华为云·云计算
清水白石0082 小时前
《用 Python 单例模式打造稳定高效的数据库连接管理器》
数据库·python·单例模式
小虾米vivian2 小时前
dmetl5 web管理平台 监控-流程监控 看不到运行信息
linux·服务器·网络·数据库·达梦数据库