淺析如何優(yōu)雅封裝.NET數(shù)據(jù)庫訪問層
但很多開發(fā)者在實(shí)際項(xiàng)目中仍然會遇到一個(gè)問題: 參數(shù)化寫法太繁瑣,代碼臃腫,開發(fā)效率低
那么有沒有一種方式可以做到:
- 寫法簡潔(接近拼接SQL)
- 自動參數(shù)化(安全)
- 統(tǒng)一規(guī)范(易維護(hù))
- 支持?jǐn)U展(日志、事務(wù)、重試)
答案是:封裝數(shù)據(jù)庫訪問層
一、為什么必須封裝
先說結(jié)論:
?? 不封裝數(shù)據(jù)庫訪問層,項(xiàng)目后期一定會亂
常見問題:
1. SQL散落各處
// A類 var sql = "SELECT * FROM A WHERE id=@id"; // B類 var sql = "SELECT * FROM A WHERE id=" + id;
寫法不統(tǒng)一,維護(hù)困難
2. 參數(shù)化風(fēng)格混亂
cmd.Parameters.AddWithValue("@id", id);
每個(gè)人寫法都不一樣
3. 無法統(tǒng)一擴(kuò)展
比如你想加:
- SQL日志
- 執(zhí)行耗時(shí)
- 重試機(jī)制
- 事務(wù)控制
需要全項(xiàng)目改代碼
所以核心目標(biāo):把所有數(shù)據(jù)庫操作收口到一層
二、設(shè)計(jì)目標(biāo)(重點(diǎn))
我們要實(shí)現(xiàn)一個(gè)工具層,滿足:
| 目標(biāo) | 說明 |
|---|---|
| 簡潔 | 類似拼接SQL寫法 |
| 安全 | 自動參數(shù)化 |
| 通用 | 支持查詢/增刪改 |
| 可擴(kuò)展 | 日志/事務(wù)/重試 |
| 易維護(hù) | 統(tǒng)一入口 |
三、推薦方案:類Dapper風(fēng)格封裝
我們實(shí)現(xiàn)一個(gè)方法:
Execute(sql, new { id = 1, name = "張三" });
看起來像拼接,但其實(shí)是參數(shù)化
四、核心實(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)點(diǎn):
- 簡潔
- 安全
- 可讀性強(qiáng)
六、進(jìn)階優(yōu)化(項(xiàng)目實(shí)戰(zhàn)重點(diǎn))
1. SQL日志(強(qiáng)烈建議)
Stopwatch sw = Stopwatch.StartNew();
var result = cmd.ExecuteNonQuery();
sw.Stop();
Console.WriteLine($"SQL執(zhí)行耗時(shí):{sw.ElapsedMilliseconds}ms");
Console.WriteLine(sql);
在MES/WMS中非常重要(排查慢SQL)
2. 異常重試機(jī)制
適用于:
- 數(shù)據(jù)庫瞬時(shí)連接失敗
- 網(wǎng)絡(luò)抖動
int retry = 3;
while (retry-- > 0)
{
try
{
return cmd.ExecuteNonQuery();
}
catch
{
if (retry == 0) throw;
Thread.Sleep(200);
}
}
3. 事務(wù)封裝
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("非法字段");
}
七、適配你當(dāng)前技術(shù)棧(重點(diǎn))
你現(xiàn)在用:
- .NET
- PostgreSQL / SQL Server
- MES / WMS
- 高并發(fā) + 設(shè)備數(shù)據(jù)
建議:
1. PostgreSQL(Npgsql)
把:
SqlCommand
換成:
NpgsqlCommand
2. 高頻接口建議
必須加:
- SQL日志
- 超時(shí)控制
- 重試機(jī)制
3. 設(shè)備數(shù)據(jù)(你做過Socket)
強(qiáng)烈建議:
- 所有入庫操作必須參數(shù)化
- 禁止拼接
八、什么時(shí)候不用自己封裝
直接說實(shí)話:
如果項(xiàng)目不復(fù)雜,直接用:
- Dapper(強(qiáng)烈推薦)
- SqlSugar(你已經(jīng)在用)
- FreeSQL
你自己封裝適合:
- 公司沒有規(guī)范
- 項(xiàng)目需要統(tǒng)一風(fē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)文章希望大家以后多多支持腳本之家!
- .NET4.7使用NLog記錄日志到數(shù)據(jù)庫表
- .NET使用C#導(dǎo)入Excel文件數(shù)據(jù)到數(shù)據(jù)庫
- 如何使用.NET8 創(chuàng)建使用MySQL數(shù)據(jù)庫的webapi項(xiàng)目
- 實(shí)現(xiàn).Net7下數(shù)據(jù)庫定時(shí)檢查的方法詳解
- ASP.NET MVC把數(shù)據(jù)庫中枚舉項(xiàng)的數(shù)字轉(zhuǎn)換成文字
- .Net?ORM?訪問?Firebird?數(shù)據(jù)庫的方法
- Asp.net core利用dynamic簡化數(shù)據(jù)庫訪問
相關(guān)文章
.NET下通過HttpListener實(shí)現(xiàn)簡單的Http服務(wù)
這篇文章主要為大家詳細(xì)介紹了.NET下通過HttpListener實(shí)現(xiàn)簡單Http服務(wù)的相關(guān)資料,感興趣的小伙伴們可以參考一下2016-09-09
.Net Core配置與自動更新的實(shí)現(xiàn)方法
下面小編就為大家分享一篇.Net Core配置與自動更新的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-12-12
asp.net中一款極為簡單實(shí)用的圖表插件(jquery)
這里介紹一款簡單實(shí)用的圖表插件,該圖表插件是基于jquery和jquery的插件 gchart很容易實(shí)現(xiàn)的,而gchart插件是封裝了google的圖表api 。2011-07-07
HTTP錯(cuò)誤500.19解決方法(定義了重復(fù)的節(jié)點(diǎn))
HTTP 錯(cuò)誤 500.19 - Internal Server Error 無法訪問請求的頁面,因?yàn)樵擁摰南嚓P(guān)配置數(shù)據(jù)無效2013-06-06
ASP.NET實(shí)現(xiàn)推送文件到瀏覽器的方法
這篇文章主要介紹了ASP.NET實(shí)現(xiàn)推送文件到瀏覽器的方法,可實(shí)現(xiàn)將文件推送到瀏覽器供用戶瀏覽或下載的功能,需要的朋友可以參考下2015-06-06
一個(gè)合格的程序員應(yīng)該讀過哪些書(偏java)
編者按:2008年8月4日,StackOverflow 網(wǎng)友 Bert F 發(fā)帖提問:哪本最具影響力的書,是每個(gè)程序員都應(yīng)該讀的2013-04-04
asp.net 臨時(shí)數(shù)據(jù)保存實(shí)現(xiàn)代碼
在一個(gè)程序運(yùn)行的時(shí)候往往我們會回到上一個(gè)頁面,或者我們會需要當(dāng)時(shí)留在這個(gè)頁面的臨時(shí)數(shù)據(jù),例如,我們用百度搜索“腳本”之后我們會看到很多那啥我就不再說了??!然后我們返回本來頁面在文本框內(nèi)會有“腳本”的字樣,我們一下代碼段就是實(shí)現(xiàn)這個(gè)功能2012-04-04

