首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

.NET 經典 SqlHelper 全解析:反射映射、泛型實體與連接管理最佳實踐

     編輯:Java 相關知識

SqlHelper 定位與設計目標

SqlHelper 是 .NET Framework 時代廣泛使用的 ADO.NET 封裝庫,旨在簡化 SqlConnection、SqlCommand、SqlDataReader 的樣板代碼,提供執行標量、非查詢、實體映射、列表查詢等高階 API。本文深度剖析其核心實現,並指出現代 .NET Core/.NET 5+ 環境下的改進方向。

核心代碼結構解讀

1. 連接管理:靜態單例模式的隱患

private static SqlConnection _con = null;
public static string constr = "數據庫連接字符串";

public static SqlConnection Con {
    get {
        if (_con == null) _con = new SqlConnection();
        if (string.IsNullOrEmpty(_con.ConnectionString)) 
            _con.ConnectionString = constr;
        return _con;
    }
    set => _con = value;
}

嚴重問題:靜態 SqlConnection 共享於所有線程,非線程安全。併發調用時會拋出「連接狀態無效」異常。正確做法是每次操作創建新連接,依賴連接池復用底層物理連接。

2. 執行標量與非查詢:標準模式

public static int ExecuteScalar(string cmdText, CommandType cmdType, params SqlParameter[] paras) {
    using (var conn = new SqlConnection(constr))
    using (var cmd = new SqlCommand(cmdText, conn)) {
        cmd.CommandType = cmdType;
        if (paras != null) cmd.Parameters.AddRange(paras);
        conn.Open();
        return Convert.ToInt32(cmd.ExecuteScalar());
    }
}

public static int ExecuteNonQuery(string cmdText, CommandType cmdType, params SqlParameter[] paras) {
    using (var conn = new SqlConnection(constr))
    using (var cmd = new SqlCommand(cmdText, conn)) {
        cmd.CommandType = cmdType;
        if (paras != null) cmd.Parameters.AddRange(paras);
        conn.Open();
        return cmd.ExecuteNonQuery();
    }
}

優點:using 確保連接釋放,參數化防 SQL 注入。建議:移除靜態連接,改為方法內 `new SqlConnection()`。

3. 反射實體映射:核心亮點與性能代價

private static T ExecuteDataReader<T>(IDataReader reader) {
    T obj = Activator.CreateInstance<T>(); // 需無參構造函數
    var props = typeof(T).GetProperties();
    foreach (var prop in props) {
        for (int i = 0; i < reader.FieldCount; i++) {
            if (reader.GetName(i).Equals(prop.Name, StringComparison.OrdinalIgnoreCase)) {
                var val = reader[prop.Name];
                if (val != null && val != DBNull.Value)
                    prop.SetValue(obj, val);
                break;
            }
        }
    }
    return obj;
}

public static T ExecuteEntity<T>(string cmdText, CommandType cmdType, params SqlParameter[] paras) {
    using (var conn = new SqlConnection(constr))
    using (var cmd = new SqlCommand(cmdText, conn)) {
        cmd.CommandType = cmdType;
        if (paras != null) cmd.Parameters.AddRange(paras);
        conn.Open();
        using (var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection)) {
            return reader.Read() ? ExecuteDataReader<T>(reader) : default;
        }
    }
}

public static List<T> ExecuteList<T>(string cmdText, CommandType cmdType, params SqlParameter[] paras) {
    var list = new List<T>();
    using (var conn = new SqlConnection(constr))
    using (var cmd = new SqlCommand(cmdText, conn)) {
        cmd.CommandType = cmdType;
        if (paras != null) cmd.Parameters.AddRange(paras);
        conn.Open();
        using (var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection)) {
            while (reader.Read()) list.Add(ExecuteDataReader<T>(reader));
        }
    }
    return list;
}

反射映射優缺點分析

優點缺點
零配置,約定優於配置 (屬性名=列名)反射開銷大:GetProperties/SetValue 比直接賦值慢 10-100 倍
泛型強類型,編譯期檢查不支援嵌套對象、異名映射、類型轉換邏輯
代碼極簡,快速開發每次查詢重複反射,無緩存機制

性能優化方案

  1. 緩存 PropertyInfo:靜態 `ConcurrentDictionary<Type, PropertyInfo[]>` 緩存屬性元數據
  2. 使用委託/Expression Tree:動態生成賦值委託,接近手寫代碼速度
  3. Dapper/EntityFramework Core:現代 ORM 內建高效映射,推薦替代

現代化改造指南 (.NET Core / .NET 5+)

1. 依賴注入注冊

// Program.cs / Startup.cs
builder.Services.AddScoped<ISqlHelper, SqlHelper>();
builder.Services.Configure<DbOptions>(cfg.GetSection("ConnectionStrings"));

2. 介面化與異步支持

public interface ISqlHelper {
    Task<int> ExecuteScalarAsync(string sql, CommandType type, params SqlParameter[] p);
    Task<int> ExecuteNonQueryAsync(string sql, CommandType type, params SqlParameter[] p);
    Task<T> QueryFirstOrDefaultAsync<T>(string sql, CommandType type, params SqlParameter[] p);
    Task<IEnumerable<T>> QueryAsync<T>(string sql, CommandType type, params SqlParameter[] p);
}

3. 配合 Dapper 使用 (推薦)

public class DapperHelper : ISqlHelper {
    private readonly string _connStr;
    public DapperHelper(IOptions<DbOptions> opt) => _connStr = opt.Value.Default;

    private IDbConnection CreateConnection() => new SqlConnection(_connStr);

    public async Task<IEnumerable<T>> QueryAsync<T>(string sql, CommandType type, params SqlParameter[] p) {
        using var conn = CreateConnection();
        var dp = new DynamicParameters();
        if (p != null) foreach (var param in p) dp.Add(param.ParameterName, param.Value);
        return await conn.QueryAsync<T>(sql, dp, commandType: type);
    }
    // 其他方法類似...
}

總結與遷移建議

  • 舊專案維護:修復靜態連接線程安全問題,添加異步方法,引入反射緩存
  • 新專案開發:直接使用 Dapper (輕量、高性能、支援多數據庫) 或 EF Core (全功能 ORM、LINQ、遷移)
  • 核心原則:連接生命週期隨業務方法、參數化防注入、異步非阻塞、映射邏輯解耦

SqlHelper 承載了 .NET 早期數據訪問的簡化智慧,但隨著生態演進,拥抱現代化工具鏈才能釋放 .NET 平台的真正性能與開發效率。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved