一、开篇导语
在.NET开发领域,System.Data.SqlClient始终是连接SQL Server数据库的黄金搭档。这个经典ADO.NET组件就像老司机的工具箱,虽然不如Entity Framework花哨,但胜在灵活高效。本文将通过六个完整示例,手把手教你从基础查询到高级应用,同时深入分析技术细节和实战经验。
二、基础环境搭建
技术栈说明:
- 开发环境:Visual Studio 2022
- 数据库:SQL Server 2019
- .NET版本:.NET 6.0
- 核心组件:System.Data.SqlClient 4.8.3
创建测试表脚本:
CREATE TABLE Employees (
ID INT PRIMARY KEY IDENTITY,
Name NVARCHAR(50),
Position NVARCHAR(50),
Salary DECIMAL(18,2),
HireDate DATETIME
);
三、核心查询方法
3.1 基础查询(SqlCommand)
// 连接字符串配置
string connectionString = "Server=.;Database=CompanyDB;Integrated Security=True;";
using (SqlConnection connection = new SqlConnection(connectionString))
{
// 打开数据库连接
connection.Open();
// 创建命令对象
string sql = "SELECT * FROM Employees WHERE Salary > @minSalary";
using (SqlCommand command = new SqlCommand(sql, connection))
{
// 添加参数防止SQL注入
command.Parameters.AddWithValue("@minSalary", 5000);
// 执行查询
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine($"ID:{reader["ID"]} 姓名:{reader["Name"]} 职位:{reader["Position"]}");
}
}
}
}
3.2 存储过程调用
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("usp_GetEmployeeByPosition", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@position", "工程师");
// 输出参数配置
SqlParameter outputParam = new SqlParameter("@totalCount", SqlDbType.Int);
outputParam.Direction = ParameterDirection.Output;
cmd.Parameters.Add(outputParam);
using (SqlDataReader dr = cmd.ExecuteReader())
{
while (dr.Read())
{
// 处理结果集
}
}
Console.WriteLine($"总记录数:{outputParam.Value}");
}
}
3.3 事务处理
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
SqlTransaction transaction = conn.BeginTransaction();
try
{
using (SqlCommand cmd = new SqlCommand(
"UPDATE Employees SET Salary = Salary * 1.1 WHERE Position = @position",
conn,
transaction))
{
cmd.Parameters.AddWithValue("@position", "经理");
int rows = cmd.ExecuteNonQuery();
Console.WriteLine($"更新了{rows}条记录");
}
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
四、关联技术详解
4.1 连接池优化
在连接字符串中添加以下配置可提升性能:
"Pooling=true;Max Pool Size=100;Min Pool Size=10;Connection Lifetime=300;"
4.2 异步查询
public async Task<List<Employee>> GetEmployeesAsync()
{
var employees = new List<Employee>();
using (var conn = new SqlConnection(connectionString))
{
await conn.OpenAsync();
using (var cmd = new SqlCommand("SELECT * FROM Employees", conn))
{
using (var reader = await cmd.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
{
employees.Add(new Employee {
ID = reader.GetInt32(0),
Name = reader.GetString(1)
});
}
}
}
}
return employees;
}
五、应用场景分析
5.1 适用场景
- 需要精细控制SQL执行的OLTP系统
- 大数据量批量操作(超过10万条记录)
- 需要直接调用存储过程的遗留系统
- 对性能要求极高的高频查询场景
5.2 不适用场景
- 需要快速开发的CRUD管理系统
- 复杂对象关系映射需求
- 多数据库支持场景
- 需要自动变更跟踪的领域
六、技术优缺点对比
6.1 优势分析
- 直接操作ADO.NET层,性能损耗最小
- 完整的事务控制能力
- 支持存储过程等高级数据库特性
- 内存占用低,适合资源受限环境
6.2 劣势注意
- 需要手动管理连接生命周期
- 缺乏强类型支持
- 需要自行处理对象映射
- 学习曲线较陡峭
七、实战注意事项
7.1 安全规范
- 必须使用参数化查询(示例3.1演示)
- 禁止拼接SQL字符串
- 存储过程要校验输入参数
- 连接字符串加密存储
7.2 性能优化
- 及时关闭DataReader对象
- 合理设置连接池参数
- 批量操作使用SqlBulkCopy
- 避免N+1查询问题
7.3 异常处理模板
try
{
// 数据库操作
}
catch (SqlException ex) when (ex.Number == 1205)
{
// 死锁重试逻辑
}
catch (SqlException ex)
{
// 通用错误处理
}
finally
{
// 资源释放
}
八、技术演进展望
虽然Entity Framework Core日益流行,但System.Data.SqlClient在以下场景仍不可替代:
- 需要直接调用特定数据库功能的场景
- 执行复杂批处理作业
- 迁移维护遗留系统
- 对性能要求达到极致的场景
九、总结建议
对于刚入门的开发者,建议从ADO.NET基础学起;对于有经验的团队,可以封装通用数据访问层。无论技术如何演进,理解底层原理永远是最有价值的投资。