C# 连接 SQL Server 数据库步骤图解

文章导读
C# 连接 SQL Server 这件事,真正要判断的不是“有没有一段能用的连接字符串”,而是三件事是否同时成立:目标实例网络可达、认证方式与账号权限匹配、项目里选用的驱动(通常是 Microsoft.Data.SqlClient 或较早的 System.Data.SqlClient)与目标框架兼容。这三条里任何一条不成立,代码写得再标准也会在 Open() 这一步失败。所以建议先把连接拆成“配置
📋 目录
  1. 一 先确认当前场景和目标
  2. 二 按这个顺序处理
  3. 三 改完后怎么验证
  4. 四 容易忽略的边界
A A

C# 连接 SQL Server 这件事,真正要判断的不是“有没有一段能用的连接字符串”,而是三件事是否同时成立:目标实例网络可达、认证方式与账号权限匹配、项目里选用的驱动(通常是 Microsoft.Data.SqlClient 或较早的 System.Data.SqlClient)与目标框架兼容。这三条里任何一条不成立,代码写得再标准也会在 Open() 这一步失败。所以建议先把连接拆成“配置—打开—执行最小查询—释放”四步,用一次最简单的查询把链路走通,再往上叠业务 SQL。

适用场景:.NET 桌面、服务或 Web 项目连接本机或内网 SQL Server。操作动作:把连接字符串放到配置文件,用 SqlConnection 打开后先执行 SELECT 1。验证方式:能取回结果,且失败时异常里能读出是登录、网络还是数据库名的问题。边界:账号密码不要硬编码进源码,加密与证书、端口与命名实例的限制需要结合自身环境确认,示例不能照搬上线。

先确认当前场景和目标

在写代码前,先确认四项信息,否则后面排查会很盲目。

  • 实例在哪:本机默认实例、命名实例(如 主机名\\SQLEXPRESS),还是远端 IP 加自定端口。命名实例通常还需要 SQL Browser 服务可解析,或者直接在连接字符串里写死端口。
  • 用什么认证:Windows 身份验证(连接字符串写 Integrated Security=True,账号是运行进程的身份)还是 SQL Server 身份验证(需要用户名密码,且实例要允许混合模式)。两者不能混填。
  • 连哪个库:Initial Catalog 指定的数据库必须存在,且该账号有访问权限,否则会报“无法打开登录所请求的数据库”。
  • 用什么驱动:新项目建议优先用 Microsoft.Data.SqlClient;老项目若已大量使用 System.Data.SqlClient,不必为了这一处强行替换,但要知道两者的加密默认值等行为可能不同,需要结合环境确认。

判断条件可以用一句话概括:如果 SSMS 或 sqlcmd 用同样的实例、同样的凭据能连上并查询,问题大概率在代码或配置;如果工具也连不上,就先解决服务器侧和网络,不要继续改 C# 代码。

按这个顺序处理

第一步,把连接字符串放进配置文件,不要写在 .cs 里。下面是一个可替换的骨架,尖括号部分按自己的环境填写:

C# 连接 SQL Server 数据库步骤图解
{
  "ConnectionStrings": {
    "Default": "Server=<主机名或IP>,<端口>;Initial Catalog=<数据库名>;User Id=<账号>;Password=<密码>;Encrypt=True;TrustServerCertificate=True;Connect Timeout=5"
  }
}

TrustServerCertificate=True 只适合自签证书的开发或内网环境;正式环境应配置可信证书后去掉这一项。Connect Timeout=5 是为了让连不上时快速失败,避免界面或接口长时间挂住。

第二步,写最小连接与查询代码,先不掺业务:

using Microsoft.Data.SqlClient;

var cs = configuration.GetConnectionString("Default");

await using var conn = new SqlConnection(cs);
await conn.OpenAsync();

await using var cmd = new SqlCommand("SELECT 1", conn);
var result = await cmd.ExecuteScalarAsync();
Console.WriteLine($"连接成功,返回值={result}");

第三步,确认链路通了,再换成参数化查询,而不是拼接字符串:

await using var cmd = new SqlCommand(
    "SELECT Id, Name FROM dbo.Users WHERE Name = @name", conn);
cmd.Parameters.Add(new SqlParameter("@name", System.Data.SqlDbType.NVarChar, 50)
{
    Value = userName
});

await using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
    Console.WriteLine(reader.GetInt32(0) + " " + reader.GetString(1));
}

第四步,把 SqlConnection、SqlCommand、SqlDataReader 都放在 using 或 await using 里,确保异常路径也能释放。连接对象释放后回连接池,不需要手动关闭两次。

C# 连接 SQL Server 数据库步骤图解

第五步,异常要分层处理:捕获 SqlException 并打印 Number 和 Message,不要只写一个“数据库连接失败”。登录失败、库不存在、网络不可达对应的错误码完全不同,能否快速定位全看这一步。

改完后怎么验证

验证动作建议按顺序做,每一步都有对应的预期信号:

  1. 运行上面的 SELECT 1,能打印出返回值 1,说明实例可达、认证通过、库可访问。
  2. 把查询换成 SELECT @@VERSION,能读到版本字符串,说明当前登录账号具备最基本的查询权限。
  3. 故意写错密码或数据库名各跑一次,观察异常信息。密码错误通常对应登录失败类错误码(如 18456),数据库名错误对应无法打开数据库(如 4060),实例名或端口不对通常是网络或实例相关错误(如 53、40)。能对上号,说明异常处理写得有效。
  4. 在业务查询上跑一次带参数的实际语句,确认返回行数与在 SSMS 里手工执行的结果一致,避免参数类型或排序规则带来的偏差。
  5. 连续调用多次,观察是否出现连接超时堆积;如果出现,先检查是否正确释放了连接对象。

预期信号是:正常路径无异常、失败路径异常可读、连接使用后能被回收。如果只有第一项通过而其余不过,说明代码骨架没问题,问题在权限或具体 SQL 上。

容易忽略的边界

  • 驱动版本差异:Microsoft.Data.SqlClient 默认加密行为与老驱动不同,从 System.Data.SqlClient 迁移时,可能需要在连接字符串里显式声明加密与证书选项,具体取值要结合服务器证书情况确认。
  • 命名实例与端口:远端访问命名实例时,如果 UDP 1434 被限制,SQL Browser 无法解析实例名,连接会超时;这时改为写死 TCP 端口通常更稳。
  • 权限最小化:连接账号只给业务需要的库和对象权限,不要为了省事给 sysadmin。权限不足时的报错和连接失败很像,容易误判成网络问题。
  • 不适用的情况:需要跨域或跨信任边界的 Windows 身份验证、需要 Always Encrypted 或特定 TLS 策略的场景,本文的示例只能作为起点,必须结合自身环境单独配置。
  • 回退建议:如果代码怎么改都不通,先回到 sqlcmd 或 SSMS,用同一份凭据验证服务器侧;服务器侧也不通,就交给网络或 DBA 处理,不要继续在 C# 里试参数。