淺析如何優(yōu)雅封裝.NET數(shù)據(jù)庫訪問層
但很多開發(fā)者在實際項目中仍然會遇到一個問題: 參數(shù)化寫法太繁瑣,代碼臃腫,開發(fā)效率低
那么有沒有一種方式可以做到:
- 寫法簡潔(接近拼接SQL)
- 自動參數(shù)化(安全)
- 統(tǒng)一規(guī)范(易維護)
- 支持擴展(日志、事務、重試)
答案是:封裝數(shù)據(jù)庫訪問層
一、為什么必須封裝
先說結(jié)論:
?? 不封裝數(shù)據(jù)庫訪問層,項目后期一定會亂
常見問題:
1. SQL散落各處
// A類 var sql = "SELECT * FROM A WHERE id=@id"; // B類 var sql = "SELECT * FROM A WHERE id=" + id;
寫法不統(tǒng)一,維護困難
2. 參數(shù)化風格混亂
cmd.Parameters.AddWithValue("@id", id);
每個人寫法都不一樣
3. 無法統(tǒng)一擴展
比如你想加:
- SQL日志
- 執(zhí)行耗時
- 重試機制
- 事務控制
需要全項目改代碼
所以核心目標:把所有數(shù)據(jù)庫操作收口到一層
二、設計目標(重點)
我們要實現(xiàn)一個工具層,滿足:
| 目標 | 說明 |
|---|---|
| 簡潔 | 類似拼接SQL寫法 |
| 安全 | 自動參數(shù)化 |
| 通用 | 支持查詢/增刪改 |
| 可擴展 | 日志/事務/重試 |
| 易維護 | 統(tǒng)一入口 |
三、推薦方案:類Dapper風格封裝
我們實現(xiàn)一個方法:
Execute(sql, new { id = 1, name = "張三" });
看起來像拼接,但其實是參數(shù)化
四、核心實現(xiàn)(通用工具類)
1. 執(zhí)行SQL(增刪改)
public static int Execute(string sql, object param = null)
{
using (var conn = new SqlConnection(connStr))
{
using (var cmd = new SqlCommand(sql, conn))
{
if (param != null)
{
AddParameters(cmd, param);
}
conn.Open();
return cmd.ExecuteNonQuery();
}
}
}
2. 查詢單條
public static T QueryFirst<T>(string sql, object param = null) where T : new()
{
using (var conn = new SqlConnection(connStr))
using (var cmd = new SqlCommand(sql, conn))
{
if (param != null)
{
AddParameters(cmd, param);
}
conn.Open();
using (var reader = cmd.ExecuteReader())
{
if (!reader.Read()) return default;
var obj = new T();
foreach (var prop in typeof(T).GetProperties())
{
if (!reader.HasColumn(prop.Name)) continue;
var val = reader[prop.Name];
if (val != DBNull.Value)
prop.SetValue(obj, val);
}
return obj;
}
}
}
3. 參數(shù)自動映射(核心)
private static void AddParameters(SqlCommand cmd, object param)
{
foreach (var prop in param.GetType().GetProperties())
{
var value = prop.GetValue(param) ?? DBNull.Value;
cmd.Parameters.AddWithValue("@" + prop.Name, value);
}
}
這一步就是關(guān)鍵:把匿名對象 → 自動轉(zhuǎn)成SQL參數(shù)
五、使用方式(對比)
舊寫法(拼接)
var sql = $"SELECT * FROM User WHERE Name='{name}'";
新寫法(推薦)
var sql = "SELECT * FROM User WHERE Name=@Name";
var user = QueryFirst<User>(sql, new { Name = name });
優(yōu)點:
- 簡潔
- 安全
- 可讀性強
六、進階優(yōu)化(項目實戰(zhàn)重點)
1. SQL日志(強烈建議)
Stopwatch sw = Stopwatch.StartNew();
var result = cmd.ExecuteNonQuery();
sw.Stop();
Console.WriteLine($"SQL執(zhí)行耗時:{sw.ElapsedMilliseconds}ms");
Console.WriteLine(sql);
在MES/WMS中非常重要(排查慢SQL)
2. 異常重試機制
適用于:
- 數(shù)據(jù)庫瞬時連接失敗
- 網(wǎng)絡抖動
int retry = 3;
while (retry-- > 0)
{
try
{
return cmd.ExecuteNonQuery();
}
catch
{
if (retry == 0) throw;
Thread.Sleep(200);
}
}
3. 事務封裝
public static void ExecuteInTransaction(Action<SqlConnection, SqlTransaction> action)
{
using var conn = new SqlConnection(connStr);
conn.Open();
using var tran = conn.BeginTransaction();
try
{
action(conn, tran);
tran.Commit();
}
catch
{
tran.Rollback();
throw;
}
}
用法:
ExecuteInTransaction((conn, tran) =>
{
Execute("INSERT ...", param1, conn, tran);
Execute("UPDATE ...", param2, conn, tran);
});
4. 防止動態(tài)SQL注入(白名單)
var allowedColumns = new[] { "Name", "Age" };
if (!allowedColumns.Contains(column))
{
throw new Exception("非法字段");
}
七、適配你當前技術(shù)棧(重點)
你現(xiàn)在用:
- .NET
- PostgreSQL / SQL Server
- MES / WMS
- 高并發(fā) + 設備數(shù)據(jù)
建議:
1. PostgreSQL(Npgsql)
把:
SqlCommand
換成:
NpgsqlCommand
2. 高頻接口建議
必須加:
- SQL日志
- 超時控制
- 重試機制
3. 設備數(shù)據(jù)(你做過Socket)
強烈建議:
- 所有入庫操作必須參數(shù)化
- 禁止拼接
八、什么時候不用自己封裝
直接說實話:
如果項目不復雜,直接用:
- Dapper(強烈推薦)
- SqlSugar(你已經(jīng)在用)
- FreeSQL
你自己封裝適合:
- 公司沒有規(guī)范
- 項目需要統(tǒng)一風格
- 想做技術(shù)沉淀
九、總結(jié)
數(shù)據(jù)庫訪問層的本質(zhì):不是為了封裝而封裝,而是為了統(tǒng)一和可控
到此這篇關(guān)于淺析如何優(yōu)雅封裝.NET數(shù)據(jù)庫訪問層的文章就介紹到這了,更多相關(guān).NET封裝數(shù)據(jù)庫訪問層內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
.NET下通過HttpListener實現(xiàn)簡單的Http服務
這篇文章主要為大家詳細介紹了.NET下通過HttpListener實現(xiàn)簡單Http服務的相關(guān)資料,感興趣的小伙伴們可以參考一下2016-09-09
HTTP錯誤500.19解決方法(定義了重復的節(jié)點)
HTTP 錯誤 500.19 - Internal Server Error 無法訪問請求的頁面,因為該頁的相關(guān)配置數(shù)據(jù)無效2013-06-06
asp.net 臨時數(shù)據(jù)保存實現(xiàn)代碼
在一個程序運行的時候往往我們會回到上一個頁面,或者我們會需要當時留在這個頁面的臨時數(shù)據(jù),例如,我們用百度搜索“腳本”之后我們會看到很多那啥我就不再說了啊!然后我們返回本來頁面在文本框內(nèi)會有“腳本”的字樣,我們一下代碼段就是實現(xiàn)這個功能2012-04-04

