
在构建数据驱动的应用程序时数据库访问层是基石。本篇文章将介绍使用C#和ADO.NET进行数据库编程的核心流程与关键概念。一、建立可靠连接连接字符串与连接对象数据库操作始于建立连接其正确性是后续所有步骤的前提。连接字符串是包含目标数据库所有定位与认证信息的配置字符串。其格式因数据库类型和验证模式而异。// SQL Server 身份验证模式示例 string connectionString Serverlocalhost;DatabaseSuperMarket;User Idsa;Password你的密码;; // Windows 集成身份验证模式示例 string connectionString Serverlocalhost;DatabaseSuperMarket;Integrated SecurityTrue;;核心类SqlConnection 用于管理物理连接。最佳实践是使用using语句确保连接被及时、可靠地关闭无论操作是否成功。// 推荐模式使用using自动管理资源 using (SqlConnection connection new SqlConnection(connectionString)) { connection.Open(); // 显式打开连接 // 在此处执行数据库操作... } // 离开using块时连接将自动、可靠地关闭二、执行数据操作SqlCommand的三种核心方法SqlCommand对象封装了要执行的SQL语句或存储过程。根据操作目的需选择其三个核心方法之一。1.ExecuteNonQuery方法用途执行不返回结果集的操作即INSERT、UPDATE、DELETE 语句。返回值受影响的行数int类型可用于判断操作是否成功。string updateSql UPDATE Commodity SET ReducedPrice price WHERE CommodityID id; using (SqlCommand command new SqlCommand(updateSql, connection)) { command.Parameters.AddWithValue(price, newPrice); command.Parameters.AddWithValue(id, productId); int rowsAffected command.ExecuteNonQuery(); if (rowsAffected 0) { // 更新成功 } }2.ExecuteScalar方法用途执行返回单行单列结果的查询。常见场景使用聚合函数如COUNT(),SUM()的查询或仅需返回单一标识符的查询。返回值一个object类型需转换为具体数据类型。string countSql SELECT COUNT(*) FROM [User] WHERE UserName name; using (SqlCommand command new SqlCommand(countSql, connection)) { command.Parameters.AddWithValue(name, userName); object result command.ExecuteScalar(); int userCount Convert.ToInt32(result); // 转换结果 }3.ExecuteReader方法用途执行返回多行多列结果集的查询。返回值一个SqlDataReader对象它提供了一种高效、只进、只读的流式方式来遍历结果。string querySql SELECT CommodityID, CommodityName, CommodityPrice FROM Commodity; using (SqlCommand command new SqlCommand(querySql, connection)) using (SqlDataReader reader command.ExecuteReader()) // 注意对Reader也使用using { while (reader.Read()) // 每次Read()前进到下一条记录 { int id reader.GetInt32(reader.GetOrdinal(CommodityID)); string name reader.GetString(reader.GetOrdinal(CommodityName)); decimal price reader.GetDecimal(reader.GetOrdinal(CommodityPrice)); // 处理当前行数据... } } // 注意在读取器打开期间其关联的连接必须保持打开状态。三、安全保障参数化查询防御SQL注入SQL注入是通过将恶意SQL代码插入到输入参数中从而欺骗服务器执行非预期命令的攻击手段。参数化查询是抵御此攻击的根本方法。绝对避免的做法字符串拼接SQL// 危险如果userInput为 admin --将注释掉后续验证导致非法登录。 string dangerousSql SELECT * FROM Users WHERE Name userInput ;正确的做法使用SqlParameter// SQL语句中使用参数名作为占位符 string safeSql SELECT * FROM [User] WHERE UserName uname AND PassWord pwd; using (SqlCommand command new SqlCommand(safeSql, connection)) { // 将用户输入的值通过参数对象传递 command.Parameters.Add(new SqlParameter(uname, userName)); command.Parameters.Add(new SqlParameter(pwd, password)); // 此时数据库会明确区分“SQL指令”和“传入的数据”恶意输入将被当作普通数据处理。 }四、资源与架构连接池与对象映射连接池是ADO.NET的一项关键性能优化技术。当调用connection.Close()或离开using块时物理连接通常不会被真正销毁而是返回到池中以备重用。这避免了频繁建立和断开连接的开销。连接字符串中的Poolingtrue;默认即启用此功能。对象关系映射是一种将数据库表记录映射到业务对象类实例的通用模式可以提高代码的可读性和可维护性。public class Commodity { public int CommodityID { get; set; } public string CommodityName { get; set; } public decimal CommodityPrice { get; set; } // ... 其他属性 } public ListCommodity GetAllCommodities() { ListCommodity list new ListCommodity(); string sql SELECT * FROM Commodity; using (SqlConnection conn new SqlConnection(connectionString)) using (SqlCommand cmd new SqlCommand(sql, conn)) { conn.Open(); using (SqlDataReader reader cmd.ExecuteReader()) { while (reader.Read()) { // 将一行数据映射为一个Commodity对象 Commodity item new Commodity { CommodityID Convert.ToInt32(reader[CommodityID]), CommodityName reader[CommodityName].ToString(), CommodityPrice Convert.ToDecimal(reader[CommodityPrice]) }; list.Add(item); } } } return list; // 返回业务对象集合而非原始DataReader或DataTable }五、常见错误在平时代码编写中会出现一些常见的错误。陷阱1连接字符串错误与连接泄漏错误连接字符串拼写错误如Data Source写成Data-Source或手动打开连接后未在finally块或异常处理中确保关闭。避坑仔细检查字符串始终使用using语句包裹SqlConnection和SqlCommand对象。陷阱2方法误用错误用ExecuteScalar去执行UPDATE语句它不会返回受影响行数或用ExecuteNonQuery去执行SELECT查询它总是返回-1。避坑根据操作意图清晰选择方法改数据用ExecuteNonQuery取单个值用ExecuteScalar取结果集用ExecuteReader。陷阱3SQL注入漏洞错误如上文所述使用字符串拼接方式构造包含用户输入的SQL。避坑无一例外地使用参数化查询。将SqlParameters.Add作为编码纪律。陷阱4空引用异常错误在触发事件或调用委托前未检查是否为null。避坑使用空条件运算符?.安全调用。// 旧方式 if (OrderCreatedEvent ! null) OrderCreatedEvent(this, args); // 推荐方式 OrderCreatedEvent?.Invoke(this, args);陷阱5忽略空字段DBNull错误直接从DataReader中读取可能为NULL的数据库字段并转换引发InvalidCastException。避坑使用IsDBNull()方法先行检查。if (!reader.IsDBNull(reader.GetOrdinal(Commission))) { commission reader.GetDecimal(reader.GetOrdinal(Commission)); }总结C#数据库访问流程通常遵循以下模式配置准备正确的连接字符串。连接在using块中创建并打开SqlConnection。命令在using块中创建SqlCommand关联连接与SQL。参数如果SQL涉及外部输入务必使用Parameters.Add添加参数。执行根据目的增删改/取单值/取集合调用相应方法。处理妥善处理返回结果。释放using语句确保所有资源被自动回收。掌握从连接管理、命令执行到安全防护的完整链条是构建可靠数据访问层的根本。