平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“浅析如何优雅封装.NET数据库访问层”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。
理解这一步时,但很多开发者在实际项目中仍然会遇到一个问题: 参数化写法太繁琐,代码臃肿,开发效率低
那么有没有一种方式能够做到:
答案是:封装数据库访问层
先说结论:
不封装数据库访问层,项目后期一定会乱
常用问题:
// A类
var sql = "SELECT * FROM A WHERE id=@id";
// B类
var sql = "SELECT * FROM A WHERE id=" + id;
写法不统一,维护困难
cmd.Parameters.AddWithValue("@id", id);
每个人写法都不一样
比如你想加:
需全项目改代码
所以核心目标:把所有数据库操作收口到一层
我们要实现一个工具层,满足:
| 目标 | 说明 |
|---|---|
| 简洁 | 类似拼接SQL写法 |
| 安全 | 自动参数化 |
| 通用 | 兼容查询/增删改 |
| 可扩展 | 日志/事务/重试 |
| 易维护 | 统一入口 |
我们实现一个方法:
Execute(sql, new { id = 1, name = "张三" });
看起来像拼接,但其实是参数化
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();
}
}
}
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;
}
}
}
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);
}
}
这一步就是关键:把匿名对象 → 自动转成SQL参数
var sql = $"SELECT * FROM User WHERE Name='{name}'";
var sql = "SELECT * FROM User WHERE Name=@Name";
var user = QueryFirst<User>(sql, new { Name = name });
优点:
Stopwatch sw = Stopwatch.StartNew();
var result = cmd.ExecuteNonQuery();
sw.Stop();
Console.WriteLine($"SQL执行耗时:{sw.ElapsedMilliseconds}ms");
Console.WriteLine(sql);
在MES/WMS中很重要(排查慢SQL)
适用来:
int retry = 3;
while (retry-- > 0)
{
try
{
return cmd.ExecuteNonQuery();
}
catch
{
if (retry == 0) throw;
Thread.Sleep(200);
}
}
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);
});
var allowedColumns = new[] { "Name", "Age" };
if (!allowedColumns.Contains(column))
{
throw new Exception("非法字段");
}
你现在用:
建议:
把:
SqlCommand
换成:
NpgsqlCommand
必须加:
强烈建议:
直接说实话:
如果项目不复杂,直接用:
你自己封装适合:
数据库访问层的本质:不是为了封装而封装,而是为了统一和可控
到此这篇关于浅析如何优雅封装.NET数据库访问层的文章就介绍到这了,更多相关.NET封装数据库访问层内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多兼容脚本之家!