c#DataTable类

在 C# 的ADO.NET中,DataTable内存中的数据表 ,是DataSet的核心组成部分,也可独立使用。它模拟了关系型数据库中 "表" 的结构,包含列定义(DataColumn)、行数据(DataRow)、约束(主键、唯一键等)和数据状态跟踪,是处理结构化数据的基础组件。

一、DataTable 的核心特性

  • 结构化存储:包含列(定义数据类型、约束)和行(存储具体数据),类似数据库表的结构。

  • 独立使用 :无需依赖DataSet,可单独创建和操作(如存储查询结果、临时数据)。

  • 状态跟踪:自动记录行的修改状态(新增、修改、删除),便于批量更新。

  • 数据验证:支持通过列约束(如非空、唯一)和自定义验证确保数据合法性。

二、DataTable 的基本结构

DataTable的核心组成如下:

  • ColumnsDataColumnCollection集合,存储表的列定义(字段名、数据类型、约束等)。

  • RowsDataRowCollection集合,存储表的行数据(每条行对应一条记录)。

  • ConstraintsConstraintCollection集合,存储约束(如主键、外键、唯一键)。

  • PrimaryKey:指定主键列(可由多个列组成复合主键),用于快速查找行。

三、常用属性与方法

类别 成员 说明
属性 TableName 获取或设置表名
Columns 获取列集合(DataColumnCollection),用于管理列定义
Rows 获取行集合(DataRowCollection),用于管理行数据
PrimaryKey 获取或设置主键列数组(DataColumn[]
DefaultView 获取DataView(表的视图),用于排序、筛选数据
方法 NewRow() 创建与表结构匹配的新行(DataRow),需通过Rows.Add()添加到表中
AcceptChanges() 提交所有行的修改(将RowState重置为Unchanged
RejectChanges() 撤销所有未提交的修改(恢复到上次AcceptChanges后的状态)
Select(string) 根据筛选条件查询行,返回DataRow[]
Compute(string, string) 计算聚合值(如SUM/COUNT),参数为表达式和筛选条件
Clear() 清空所有行数据(保留列结构)

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

以下通过 "产品表" 示例,展示DataTable的创建、结构定义、数据操作、约束管理等核心功能。

1. 创建 DataTable 并定义列结构
复制代码
// 1. 创建DataTable(指定表名)
DataTable productTable = new DataTable("Products");
​
// 2. 定义列(DataColumn)
// 产品ID(主键,int类型,非空)
DataColumn idColumn = new DataColumn("ProductId", typeof(int))
{
    AllowDBNull = false, // 不允许为空
    Unique = true // 唯一约束(主键依赖此属性)
};
productTable.Columns.Add(idColumn);
​
// 产品名称(string类型,长度50)
DataColumn nameColumn = new DataColumn("ProductName", typeof(string))
{
    MaxLength = 50, // 最大长度
    AllowDBNull = false
};
productTable.Columns.Add(nameColumn);
​
// 价格(decimal类型,默认值0)
DataColumn priceColumn = new DataColumn("Price", typeof(decimal))
{
    DefaultValue = 0m // 默认值
};
productTable.Columns.Add(priceColumn);
​
// 库存(int类型,最小值0)
DataColumn stockColumn = new DataColumn("Stock", typeof(int));
productTable.Columns.Add(stockColumn);
​
// 3. 设置主键(可多列组成复合主键)
productTable.PrimaryKey = new[] { idColumn };
​
// 验证结构
Console.WriteLine($"表名:{productTable.TableName}");
Console.WriteLine($"列数量:{productTable.Columns.Count}"); // 输出:4
Console.WriteLine($"主键列:{productTable.PrimaryKey[0].ColumnName}"); // 输出:ProductId
2. 操作行数据(添加、修改、删除)
复制代码
// 1. 添加行数据
// 方法1:通过NewRow()创建行(推荐,自动匹配列结构)
DataRow row1 = productTable.NewRow();
row1["ProductId"] = 1;
row1["ProductName"] = "笔记本电脑";
row1["Price"] = 5999.99m;
row1["Stock"] = 100;
productTable.Rows.Add(row1);
​
// 方法2:直接传值(按列顺序,需确保类型匹配)
productTable.Rows.Add(2, "鼠标", 99.99m, 500);
productTable.Rows.Add(3, "键盘", 199.99m, 300);
​
// 2. 修改行数据
DataRow laptopRow = productTable.Rows.Find(1); // 通过主键查找行
if (laptopRow != null)
{
    laptopRow["Price"] = 5799.99m; // 降价
    laptopRow["Stock"] = 95; // 减少库存
}
​
// 3. 删除行(标记删除,需AcceptChanges确认)
DataRow keyboardRow = productTable.Rows.Find(3);
if (keyboardRow != null)
{
    productTable.Rows.Remove(keyboardRow); // 直接移除(立即生效)
    // 或标记删除:keyboardRow.Delete();(需AcceptChanges后才移除)
}
​
// 4. 查看行状态(跟踪修改)
foreach (DataRow row in productTable.Rows)
{
    Console.WriteLine(
        $"ID: {row["ProductId"]}, 名称: {row["ProductName"]}, " +
        $"状态: {row.RowState}" // 输出:Modified/Unchanged
    );
}
3. 数据查询与计算
复制代码
// 1. 按条件查询行(Select方法)
// 示例:查询价格>1000的产品
DataRow[] expensiveProducts = productTable.Select("Price > 1000");
Console.WriteLine("价格>1000的产品:");
foreach (DataRow row in expensiveProducts)
{
    Console.WriteLine($"{row["ProductName"]} - {row["Price"]}元");
}
​
// 2. 排序查询(按库存降序)
DataRow[] sortedByStock = productTable.Select("", "Stock DESC"); // 第二个参数为排序表达式
​
// 3. 聚合计算(Compute方法)
// 示例:计算总库存
object totalStock = productTable.Compute("SUM(Stock)", ""); // 第二个参数为筛选条件(空表示全部)
Console.WriteLine($"总库存:{totalStock}"); // 输出:100+500=600(因键盘已被移除)
​
// 示例:计算平均价格(仅统计价格>100的产品)
object avgPrice = productTable.Compute("AVG(Price)", "Price > 100");
Console.WriteLine($"平均价格(>100):{avgPrice}"); // 输出:(5799.99)/1=5799.99
4. 数据约束与验证
复制代码
// 1. 添加唯一约束(确保产品名称不重复)
UniqueConstraint nameUnique = new UniqueConstraint("UK_ProductName", nameColumn);
productTable.Constraints.Add(nameUnique);
​
// 2. 添加检查约束(库存不能为负数)
CheckConstraint stockCheck = new CheckConstraint("CK_Stock", "Stock >= 0");
productTable.Constraints.Add(stockCheck);
​
// 3. 测试约束(触发异常)
try
{
    // 尝试添加重复名称的产品(违反唯一约束)
    productTable.Rows.Add(4, "笔记本电脑", 4999.99m, 50);
}
catch (ConstraintException ex)
{
    Console.WriteLine($"约束错误:{ex.Message}"); // 输出:列ProductName的值必须唯一
}
​
try
{
    // 尝试设置库存为负数(违反检查约束)
    DataRow mouseRow = productTable.Rows.Find(2);
    mouseRow["Stock"] = -10;
}
catch (ConstraintException ex)
{
    Console.WriteLine($"约束错误:{ex.Message}"); // 输出:CK_Stock约束失败
}
5. 与数据库交互(结合 DataAdapter)

DataTable常通过DataAdapter与数据库同步,实现数据加载和更新:

复制代码
string connectionString = "Server=.;Database=ShopDB;Integrated Security=True;";
​
// 1. 从数据库加载数据到DataTable
using (SqlDataAdapter adapter = new SqlDataAdapter(
    "SELECT ProductId, ProductName, Price, Stock FROM Products", 
    connectionString))
{
    DataTable dbProducts = new DataTable();
    adapter.Fill(dbProducts); // 自动填充数据并创建列结构
    Console.WriteLine($"从数据库加载 {dbProducts.Rows.Count} 条产品数据");
}
​
// 2. 将修改更新回数据库
using (SqlDataAdapter adapter = new SqlDataAdapter())
{
    adapter.SelectCommand = new SqlCommand(
        "SELECT ProductId, ProductName, Price, Stock FROM Products", 
        new SqlConnection(connectionString)
    );
    // 自动生成增删改命令
    SqlCommandBuilder cmdBuilder = new SqlCommandBuilder(adapter);
​
    // 假设已修改内存中的productTable
    int updatedRows = adapter.Update(productTable); // 根据RowState批量更新
    Console.WriteLine($"成功更新 {updatedRows} 条数据到数据库");
}
6. 绑定到 UI 控件

DataTable可直接绑定到 WinForm/WPF 的 UI 控件(如DataGridView),实现数据展示:

复制代码
// WinForm示例:绑定到DataGridView
// dataGridView1.DataSource = productTable;
​
// 或通过DataView绑定(支持排序/筛选)
DataView productView = productTable.DefaultView;
productView.Sort = "Price ASC"; // 按价格升序
productView.RowFilter = "Stock > 0"; // 只显示有库存的产品
// dataGridView1.DataSource = productView;

五、适用场景与注意事项

适用场景:
  • 内存中临时存储结构化数据(如查询结果、用户输入的批量数据)。

  • 简单数据关系处理 (单表操作,或与其他DataTable通过DataRelation关联)。

  • UI 数据绑定 (快速将数据展示到DataGridView等控件)。

  • 小规模数据的批量处理(如导入 Excel 数据后在内存中校验和转换)。

注意事项:
  • 内存占用 :数据全部加载到内存,不适合处理超大量数据(建议用DataReader流式读取)。

  • 数据类型匹配:添加行时需确保值的类型与列定义一致,否则会抛出类型异常。

  • 约束性能:过多约束会影响数据操作性能,复杂验证建议在业务层实现。

  • 状态管理AcceptChanges()会清除修改状态,如需后续更新数据库,应在更新后再调用。

六、总结

DataTable是处理内存中结构化数据的核心工具,其设计模拟了关系型数据库表的特性,提供了完整的列定义、行操作、约束管理和状态跟踪能力。无论是独立使用存储临时数据,还是与DataSetDataAdapter配合实现离线数据处理,DataTable都能简化数据操作流程,是 C# 数据处理中的基础组件。实际开发中,需根据数据量和业务复杂度选择使用,平衡便利性和性能。

相关推荐
风月歌2 小时前
基于微信小程序的学习资料销售平台源代码(源码+文档+数据库)
java·数据库·mysql·微信小程序·小程序·毕业设计·源码
gjc5922 小时前
【一次线上 MySQL 死锁问题的完整复盘与解析】
数据库·mysql·死锁
qq2439201612 小时前
mysql导致的内存泄漏Abandoned connection cleanup thread
数据库·mysql
·云扬·2 小时前
深入理解MySQL InnoDB MVCC:原理、实验与实践
数据库·mysql
缺点内向2 小时前
如何在 C# .NET 中将 Markdown 转换为 PDF 和 Excel:完整指南
pdf·c#·.net·excel
IvorySQL2 小时前
版本发布| IvorySQL 5.1 发布
数据库·人工智能·postgresql·开源
CodeCraft Studio2 小时前
Excel处理控件Aspose.Cells教程:使用C#在Excel中创建旭日图
c#·excel·aspose·excel旭日图·excel库·excel开发控件·excel api库
yuniko-n2 小时前
【MySQL】通俗易懂的 MVCC 与事务
数据库·后端·sql·mysql
民乐团扒谱机2 小时前
【微实验】仿AU音频编辑器开发实践:从零构建音频可视化工具
算法·c#·仿真·audio·fft·频谱