当前位置: 首页 > news >正文

C#数据库编程核心指南:从连接到安全的完整实践

在构建数据驱动的应用程序时,数据库访问层是基石。本篇文章将介绍使用C#和ADO.NET进行数据库编程的核心流程与关键概念。

一、建立可靠连接:连接字符串与连接对象

数据库操作始于建立连接,其正确性是后续所有步骤的前提。

连接字符串是包含目标数据库所有定位与认证信息的配置字符串。其格式因数据库类型和验证模式而异。

// SQL Server 身份验证模式示例 string connectionString = "Server=localhost;Database=SuperMarket;User Id=sa;Password=你的密码;"; // Windows 集成身份验证模式示例 string connectionString = "Server=localhost;Database=SuperMarket;Integrated Security=True;";

核心类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块时,物理连接通常不会被真正销毁,而是返回到池中以备重用。这避免了频繁建立和断开连接的开销。连接字符串中的Pooling=true;(默认)即启用此功能。

对象关系映射是一种将数据库表记录映射到业务对象(类实例)的通用模式,可以提高代码的可读性和可维护性。

public class Commodity { public int CommodityID { get; set; } public string CommodityName { get; set; } public decimal CommodityPrice { get; set; } // ... 其他属性 } public List<Commodity> GetAllCommodities() { List<Commodity> list = new List<Commodity>(); 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语句包裹SqlConnectionSqlCommand对象。

陷阱2:方法误用

  • 错误:用ExecuteScalar去执行UPDATE语句(它不会返回受影响行数),或用ExecuteNonQuery去执行SELECT查询(它总是返回-1)。

  • 避坑:根据操作意图清晰选择方法:改数据用ExecuteNonQuery,取单个值用ExecuteScalar,取结果集用ExecuteReader

陷阱3:SQL注入漏洞

  • 错误:如上文所述,使用字符串拼接方式构造包含用户输入的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#数据库访问流程通常遵循以下模式:

  1. 配置:准备正确的连接字符串。

  2. 连接:在using块中创建并打开SqlConnection

  3. 命令:在using块中创建SqlCommand,关联连接与SQL。

  4. 参数如果SQL涉及外部输入,务必使用Parameters.Add添加参数。

  5. 执行:根据目的(增删改/取单值/取集合)调用相应方法。

  6. 处理:妥善处理返回结果。

  7. 释放using语句确保所有资源被自动回收。

掌握从连接管理、命令执行到安全防护的完整链条,是构建可靠数据访问层的根本。

http://www.jsqmd.com/news/519988/

相关文章:

  • 水墨江南模型爬虫数据清洗后处理:提升生成素材质量
  • UE4 后处理材质实战:从ShaderToy移植高级镜头光晕效果
  • tmux实战使用
  • 开源模型Wan2.2-TI2V-5B:从技术原理到场景创新的全流程应用指南
  • 面试问题controller和service能不能互相替换
  • 列线图/诺莫图 含概率轴
  • 基于云平台的毕业设计:从零构建高可用 Web 应用的技术选型与实践
  • GlyEngine:嵌入式Lua引擎的零堆内存与跨平台实现
  • Java多线程学习(五)
  • AutoGen Studio效果展示:Qwen3-4B-Instruct-2507驱动的自动化法律合同审查Agent案例
  • 前端学习之HTML5+CSS3基础
  • 嵌入式OBDII CAN驱动库:物理层接入与多帧解析实战
  • 手把手教你用R-Studio Technician搭建个人数据恢复工作站(附硬件清单与避坑指南)
  • 多分类任务中SHAP特征重要性可视化:从summary_plot报错到实战调优
  • 嵌入式OTA升级失败率下降83%的关键:用C语言实现可验证状态机(含完整FSM状态迁移图与超时自愈逻辑)
  • cv_resnet18_ocr-detection ONNX导出实战:跨平台部署,C++/Java都能用
  • K8S 控制器-资源调度-叩丁狼
  • 魔搭社区实战:零代码玩转Qwen2.5-3B-Instruct-GGUF云端部署
  • 告别C#,我用Python+PyCharm+AutoCAD搞定了CAD二次开发(附完整代码)
  • GLM-OCR多模态识别模型:从零开始快速部署与测试
  • MuMu模拟器+Edge浏览器:H5页面元素定位的实战指南
  • 终极指南:如何用StardewXnbHack一键解锁《星露谷物语》所有游戏资源
  • FeRAM嵌入式驱动库:轻量、实时、线程安全的铁电存储控制方案
  • Qwen3-0.6B-FP8企业应用:法律合同初稿生成+关键条款提示双模协同案例
  • 第五章 渲染管线
  • 液驱剪叉电气系统拆解:从ECU到线束的保姆级指南(附LGMG AS1212实例)
  • Alpamayo-R1-10B作品集:10组高难度长尾场景(鬼探头、视线遮挡、异形车辆)应对案例
  • OpenClaw多终端控制:通过飞书管理GLM-4.7-Flash任务实践
  • 别再乱接AGND和DGND了!手把手教你搞定数模混合电路的地平面设计(附PCB布局实例)
  • MogFace人脸检测模型-WebUI作品:100%覆盖侧脸/低头/遮挡/运动模糊的挑战样本集