如何优雅封装.NET数据库访问层(彻底告别拼接SQL)

张开发
2026/4/22 5:45:30 15 分钟阅读

分享文章

如何优雅封装.NET数据库访问层(彻底告别拼接SQL)
【进阶篇】如何优雅封装.NET数据库访问层彻底告别拼接SQL在上一篇中我们已经分析了为什么不建议使用SQL拼接而应该使用参数化查询但很多开发者在实际项目中仍然会遇到一个问题 参数化写法太繁琐代码臃肿开发效率低那么有没有一种方式可以做到写法简洁接近拼接SQL自动参数化安全统一规范易维护支持扩展日志、事务、重试答案是封装数据库访问层一、为什么必须封装先说结论不封装数据库访问层项目后期一定会乱常见问题1. SQL散落各处// A类varsqlSELECT * FROM A WHERE idid;// B类varsqlSELECT * FROM A WHERE idid; 写法不统一维护困难2. 参数化风格混乱cmd.Parameters.AddWithValue(id,id); 每个人写法都不一样3. 无法统一扩展比如你想加SQL日志执行耗时重试机制事务控制 需要全项目改代码 ❌ 所以核心目标把所有数据库操作收口到一层二、设计目标重点我们要实现一个工具层满足目标说明简洁类似拼接SQL写法安全自动参数化通用支持查询/增删改可扩展日志/事务/重试易维护统一入口三、推荐方案类Dapper风格封装我们实现一个方法Execute(sql,new{id1,name张三}); 看起来像拼接但其实是参数化四、核心实现通用工具类1. 执行SQL增删改publicstaticintExecute(stringsql,objectparamnull){using(varconnnewSqlConnection(connStr)){using(varcmdnewSqlCommand(sql,conn)){if(param!null){AddParameters(cmd,param);}conn.Open();returncmd.ExecuteNonQuery();}}}2. 查询单条publicstaticTQueryFirstT(stringsql,objectparamnull)whereT:new(){using(varconnnewSqlConnection(connStr))using(varcmdnewSqlCommand(sql,conn)){if(param!null){AddParameters(cmd,param);}conn.Open();using(varreadercmd.ExecuteReader()){if(!reader.Read())returndefault;varobjnewT();foreach(varpropintypeof(T).GetProperties()){if(!reader.HasColumn(prop.Name))continue;varvalreader[prop.Name];if(val!DBNull.Value)prop.SetValue(obj,val);}returnobj;}}}3. 参数自动映射核心privatestaticvoidAddParameters(SqlCommandcmd,objectparam){foreach(varpropinparam.GetType().GetProperties()){varvalueprop.GetValue(param)??DBNull.Value;cmd.Parameters.AddWithValue(prop.Name,value);}} 这一步就是关键把匿名对象 → 自动转成SQL参数五、使用方式对比旧写法拼接 ❌varsql$SELECT * FROM User WHERE Name{name};新写法推荐 ✅varsqlSELECT * FROM User WHERE NameName;varuserQueryFirstUser(sql,new{Namename}); 优点简洁安全可读性强六、进阶优化项目实战重点1. SQL日志强烈建议StopwatchswStopwatch.StartNew();varresultcmd.ExecuteNonQuery();sw.Stop();Console.WriteLine($SQL执行耗时{sw.ElapsedMilliseconds}ms);Console.WriteLine(sql); 在MES/WMS中非常重要排查慢SQL2. 异常重试机制适用于数据库瞬时连接失败网络抖动intretry3;while(retry--0){try{returncmd.ExecuteNonQuery();}catch{if(retry0)throw;Thread.Sleep(200);}}3. 事务封装publicstaticvoidExecuteInTransaction(ActionSqlConnection,SqlTransactionaction){usingvarconnnewSqlConnection(connStr);conn.Open();usingvartranconn.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. 防止动态SQL注入白名单varallowedColumnsnew[]{Name,Age};if(!allowedColumns.Contains(column)){thrownewException(非法字段);}七、适配你当前技术栈重点你现在用.NETPostgreSQL / SQL ServerMES / WMS高并发 设备数据 建议1. PostgreSQLNpgsql把SqlCommand换成NpgsqlCommand2. 高频接口建议 必须加SQL日志超时控制重试机制3. 设备数据你做过Socket 强烈建议所有入库操作必须参数化禁止拼接八、什么时候不用自己封装直接说实话 如果项目不复杂直接用Dapper强烈推荐SqlSugar你已经在用FreeSQL 你自己封装适合公司没有规范项目需要统一风格想做技术沉淀九、总结 数据库访问层的本质不是为了封装而封装而是为了统一和可控最后一句给你点“实战味”的结尾在小项目里 拼接SQL你可能感觉不到问题但在生产环境中MES系统多设备接入多系统交互 一旦SQL失控不是报错的问题是直接影响生产。

更多文章