一、开篇导语

在.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在以下场景仍不可替代:

  1. 需要直接调用特定数据库功能的场景
  2. 执行复杂批处理作业
  3. 迁移维护遗留系统
  4. 对性能要求达到极致的场景

九、总结建议

对于刚入门的开发者,建议从ADO.NET基础学起;对于有经验的团队,可以封装通用数据访问层。无论技术如何演进,理解底层原理永远是最有价值的投资。