一个针对 .NET Framework 4.7.2 优化过的经典 SqlServerHelper 助手类。它基于 ADO.NET 原生开发,完整实现了防 SQL 注入的参数化查询、事务处理、存储过程调用以及资源自动释放(using 结构)。
SqlServerHelper.cs 源码
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
public class SqlServerHelper
{
// 从配置文件中读取连接字符串,如果读取不到则使用备用字符串
private static readonly string connString = ConfigurationManager.ConnectionStrings["SqlConn"]?.ConnectionString
?? "Data Source=.;Initial Catalog=YourDB;Integrated Security=True;";
#region ExecuteNonQuery (增、删、改)
/// <summary>
/// 执行增删改语句,返回受影响的行数
/// </summary>
public static int ExecuteNonQuery(string cmdText, params SqlParameter[] parameters)
{
using (SqlConnection conn = new SqlConnection(connString))
{
using (SqlCommand cmd = new SqlCommand())
{
PrepareCommand(cmd, conn, null, CommandType.Text, cmdText, parameters);
int rows = cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
return rows;
}
}
}
#endregion
#region ExecuteScalar & Exists (查单值与存在性验证)
/// <summary>
/// 执行查询,返回结果集中的第一行第一列(object 类型)
/// </summary>
public static object ExecuteScalar(string cmdText, params SqlParameter[] parameters)
{
using (SqlConnection conn = new SqlConnection(connString))
{
using (SqlCommand cmd = new SqlCommand())
{
PrepareCommand(cmd, conn, null, CommandType.Text, cmdText, parameters);
object val = cmd.ExecuteScalar();
cmd.Parameters.Clear();
return val;
}
}
}
/// <summary>
/// 执行查询,返回强类型的结果(自动处理 DBNull 与类型转换)
/// </summary>
public static T ExecuteScalar<T>(string cmdText, params SqlParameter[] parameters)
{
object val = ExecuteScalar(cmdText, parameters);
if (val == null || val == DBNull.Value)
{
return default(T);
}
// 处理 Nullable<T> (如 int?, DateTime? 等)
Type type = typeof(T);
if (Nullable.GetUnderlyingType(type) != null)
{
type = Nullable.GetUnderlyingType(type);
}
return (T)Convert.ChangeType(val, type);
}
/// <summary>
/// 检查记录是否存在 (基于查询结果的第一行第一列是否为空)
/// 建议 SQL 写法: SELECT 1 FROM Table WHERE ...
/// </summary>
public static bool Exists(string cmdText, params SqlParameter[] parameters)
{
object val = ExecuteScalar(cmdText, parameters);
// 只要结果不为 null 且不为 DBNull,即代表存在记录
return val != null && val != DBNull.Value;
}
#endregion
#region ExecuteReader (高性能流式读取)
/// <summary>
/// 执行查询,返回 SqlDataReader
/// 注意:调用方必须使用 using() 包裹返回值,或手动 Close(),否则会导致数据库连接泄露!
/// </summary>
public static SqlDataReader ExecuteReader(string cmdText, params SqlParameter[] parameters)
{
// 这里千万不能用 using(SqlConnection conn = ...),否则连接关闭后 Reader 将无法读取数据
SqlConnection conn = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand();
try
{
PrepareCommand(cmd, conn, null, CommandType.Text, cmdText, parameters);
// CommandBehavior.CloseConnection 的作用:
// 告诉 Reader:当你被关闭 (Reader.Close/Dispose) 时,连同背后的 SqlConnection 一起关闭!
SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
cmd.Parameters.Clear();
return reader;
}
catch
{
// 如果在执行过程中发生异常,必须手动关闭连接并释放资源
conn.Close();
cmd.Dispose();
throw;
}
}
#endregion
#region ExecuteDataTable & ExecuteDataSet (查表格/多表)
/// <summary>
/// 执行查询,返回 DataTable
/// </summary>
public static DataTable ExecuteDataTable(string cmdText, params SqlParameter[] parameters)
{
using (SqlConnection conn = new SqlConnection(connString))
{
using (SqlCommand cmd = new SqlCommand())
{
PrepareCommand(cmd, conn, null, CommandType.Text, cmdText, parameters);
using (SqlDataAdapter da = new SqlDataAdapter(cmd))
{
DataTable dt = new DataTable();
da.Fill(dt);
cmd.Parameters.Clear();
return dt;
}
}
}
}
/// <summary>
/// 执行查询,返回 DataSet(多表结果集)
/// </summary>
public static DataSet ExecuteDataSet(string cmdText, params SqlParameter[] parameters)
{
using (SqlConnection conn = new SqlConnection(connString))
{
using (SqlCommand cmd = new SqlCommand())
{
PrepareCommand(cmd, conn, null, CommandType.Text, cmdText, parameters);
using (SqlDataAdapter da = new SqlDataAdapter(cmd))
{
DataSet ds = new DataSet();
da.Fill(ds);
cmd.Parameters.Clear();
return ds;
}
}
}
}
#endregion
#region 存储过程支持 (Stored Procedure)
/// <summary>
/// 执行存储过程,返回 DataTable
/// </summary>
public static DataTable ExecuteStoredProcedure(string procName, params SqlParameter[] parameters)
{
using (SqlConnection conn = new SqlConnection(connString))
{
using (SqlCommand cmd = new SqlCommand())
{
PrepareCommand(cmd, conn, null, CommandType.StoredProcedure, procName, parameters);
using (SqlDataAdapter da = new SqlDataAdapter(cmd))
{
DataTable dt = new DataTable();
da.Fill(dt);
cmd.Parameters.Clear();
return dt;
}
}
}
}
#endregion
#region 事务支持 (Transaction)
/// <summary>
/// 执行多条 SQL 语句(带事务),全部成功返回 true,任一失败自动回滚并抛出异常
/// </summary>
public static bool ExecuteTransaction(List<SqlCommandInfo> commandList)
{
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
using (SqlTransaction trans = conn.BeginTransaction())
{
using (SqlCommand cmd = new SqlCommand())
{
try
{
foreach (var cmdInfo in commandList)
{
PrepareCommand(cmd, conn, trans, CommandType.Text, cmdInfo.CommandText, cmdInfo.Parameters);
cmd.ExecuteNonQuery();
cmd.Parameters.Clear(); // 每次循环前清空参数,防止冲突
}
trans.Commit();
return true;
}
catch (Exception)
{
trans.Rollback();
throw; // 向上抛出异常,便于调用方捕捉错误日志
}
}
}
}
}
#endregion
#region 内部辅助方法 (参数预处理)
/// <summary>
/// 构建并准备好 SqlCommand 状态
/// </summary>
private static void PrepareCommand(SqlCommand cmd, SqlConnection conn, SqlTransaction trans, CommandType cmdType, string cmdText, SqlParameter[] cmdParms)
{
if (conn.State != ConnectionState.Open)
conn.Open();
cmd.Connection = conn;
cmd.CommandText = cmdText;
cmd.CommandType = cmdType;
if (trans != null)
cmd.Transaction = trans;
if (cmdParms != null)
{
foreach (SqlParameter parm in cmdParms)
{
// 优雅处理 C# 的 null 转化为数据库的 DBNull.Value
if (parm.Value == null)
parm.Value = DBNull.Value;
cmd.Parameters.Add(parm);
}
}
}
#endregion
}
/// <summary>
/// 专用于事务批量执行的命令结构体
/// </summary>
public class SqlCommandInfo
{
public string CommandText { get; set; }
public SqlParameter[] Parameters { get; set; }
public SqlCommandInfo(string cmdText, SqlParameter[] parameters)
{
this.CommandText = cmdText;
this.Parameters = parameters;
}
}
App.config / Web.config 配置
在使用该类前,请确保在配置文件中添加了连接字符串:
<connectionStrings>
<add name="SqlConn" connectionString="Data Source=你的服务器地址;Initial Catalog=数据库名;User ID=用户名;Password=密码;" providerName="System.Data.SqlClient" />
</connectionStrings>
常用功能调用示例
以下是该助手类在日常开发中的具体使用方式:
插入/更新/删除 数据 (ExecuteNonQuery)
string sql = "INSERT INTO Users (UserName, Age, CreateTime) VALUES (@Name, @Age, @CreateTime)";
SqlParameter[] paras = {
new SqlParameter("@Name", "张三"),
new SqlParameter("@Age", 25),
new SqlParameter("@CreateTime", DateTime.Now)
};
int rows = SqlServerHelper.ExecuteNonQuery(sql, paras);
Console.WriteLine($"成功插入 {rows} 行数据");
查询单个值 (ExecuteScalar)
string sql = "SELECT COUNT(1) FROM Users WHERE Age > @Age";
SqlParameter[] paras = {
new SqlParameter("@Age", 18)
};
int count = Convert.ToInt32(SqlServerHelper.ExecuteScalar(sql, paras));
查询单个值 ExecuteScalar<T>
再也不用每次都在业务代码里写烦人的 Convert.ToInt32() 或者判断 DBNull 了。
string sql = "SELECT MAX(Age) FROM Users";
// 自动转换并返回强类型的 int,如果表里没数据会返回 default(int) 也就是 0
int maxAge = SqlServerHelper.ExecuteScalar<int>(sql);
string sql2 = "SELECT UserName FROM Users WHERE Id = @Id";
// 自动转换为 string,找不到返回 null
string name = SqlServerHelper.ExecuteScalar<string>(sql2, new SqlParameter("@Id", 1));
检查记录是否存在 Exists
最适合用来做注册时验证用户名是否重复、或者操作前验证数据是否存在。
// 技巧:写 SELECT 1 性能最高,不要写 SELECT *
string sql = "SELECT 1 FROM Users WHERE UserName = @Name";
SqlParameter[] paras = { new SqlParameter("@Name", "Admin") };
if (SqlServerHelper.Exists(sql, paras))
{
Console.WriteLine("用户名已存在!");
}
使用 ExecuteReader 极速读取大数据集!切记必须用 using 包裹。
string sql = "SELECT Id, UserName FROM Users WHERE Status = 1";
// 必须在 UI/BLL 层套上 using,这样 Reader 读完被释放时,连带关闭底层的 SqlConnection
using (SqlDataReader reader = SqlServerHelper.ExecuteReader(sql))
{
while (reader.Read())
{
int id = reader.GetInt32(0);
string name = reader.GetString(1);
Console.WriteLine($"ID: {id}, Name: {name}");
}
}
获取表格数据 (ExecuteDataTable)
string sql = "SELECT * FROM Users WHERE UserName LIKE @Name + '%'";
SqlParameter[] paras = {
new SqlParameter("@Name", "张")
};
DataTable dt = SqlServerHelper.ExecuteDataTable(sql, paras);
foreach (DataRow row in dt.Rows)
{
Console.WriteLine(row["UserName"].ToString());
}
执行多条 SQL 事务 (ExecuteTransaction)
var list = new List<SqlCommandInfo>();
// 语句 1
string sql1 = "UPDATE Accounts SET Balance = Balance - @Amount WHERE AccountId = @Id1";
SqlParameter[] para1 = { new SqlParameter("@Amount", 100), new SqlParameter("@Id1", 1) };
list.Add(new SqlCommandInfo(sql1, para1));
// 语句 2
string sql2 = "UPDATE Accounts SET Balance = Balance + @Amount WHERE AccountId = @Id2";
SqlParameter[] para2 = { new SqlParameter("@Amount", 100), new SqlParameter("@Id2", 2) };
list.Add(new SqlCommandInfo(sql2, para2));
try
{
bool isSuccess = SqlServerHelper.ExecuteTransaction(list);
if (isSuccess) Console.WriteLine("转账成功!");
}
catch (Exception ex)
{
Console.WriteLine($"转账失败,事务已安全回滚。错误详情: {ex.Message}");
}
ExecuteDataSet 最大的杀手锏在于“一次数据库往返,获取多个结果集”。
当你需要在同一个页面或同一个业务逻辑中,同时查询多张不相关的表(例如:同时查出一个用户的“基本信息”和他的“近期订单列表”)时,它是最高效的选择。
场景说明
我们需要在一个“用户详情页”展示数据。为了减少与数据库的网络交互次数,我们编写了一段包含两个 SELECT 的批量 SQL 语句,一次性把用户信息和订单信息全部抓取回来。
using System;
using System.Data;
using System.Data.SqlClient;
public class UserService
{
public void PrintUserDetailAndOrders(int targetUserId)
{
// 巧妙利用批量 SQL 语句,用分号或直接换行隔开多个 SELECT
string sql = @"
-- 第 1 个结果集:获取用户的基本资料
SELECT UserId, UserName, Email, Status
FROM Users
WHERE UserId = @UserId;
-- 第 2 个结果集:获取该用户的最近 5 笔订单记录
SELECT TOP 5 OrderId, OrderDate, TotalAmount
FROM Orders
WHERE UserId = @UserId
ORDER BY OrderDate DESC;
";
SqlParameter[] paras = {
new SqlParameter("@UserId", targetUserId)
};
// 调用助手类,返回包含多张表的 DataSet
DataSet ds = SqlServerHelper.ExecuteDataSet(sql, paras);
// 防御性编程:确保 DataSet 不为空,且确实包含我们要的 2 张表
if (ds != null && ds.Tables.Count >= 2)
{
// --- 处理第一张表 (索引为 0) : 用户信息 ---
DataTable dtUser = ds.Tables[0];
if (dtUser.Rows.Count > 0)
{
DataRow userRow = dtUser.Rows[0];
Console.WriteLine("=== 用户基本信息 ===");
Console.WriteLine($"用户ID: {userRow["UserId"]}");
Console.WriteLine($"用户名: {userRow["UserName"]}");
Console.WriteLine($"邮 箱: {userRow["Email"]}");
}
else
{
Console.WriteLine("未找到该用户。");
return; // 用户不存在,直接退出
}
Console.WriteLine("\n-------------------------\n");
// --- 处理第二张表 (索引为 1) : 订单列表 ---
DataTable dtOrders = ds.Tables[1];
Console.WriteLine($"=== 最近订单 ({dtOrders.Rows.Count} 笔) ===");
if (dtOrders.Rows.Count > 0)
{
foreach (DataRow row in dtOrders.Rows)
{
// 强转类型或直接格式化输出
int orderId = Convert.ToInt32(row["OrderId"]);
DateTime orderDate = Convert.ToDateTime(row["OrderDate"]);
decimal amount = Convert.ToDecimal(row["TotalAmount"]);
Console.WriteLine($"订单号: {orderId} | 日期: {orderDate:yyyy-MM-dd HH:mm} | 金额: ¥{amount}");
}
}
else
{
Console.WriteLine("该用户暂无订单记录。");
}
}
else
{
Console.WriteLine("数据查询异常,未返回预期的结果集。");
}
}
}