博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
用了Dapper之后通篇还是SqlConnection,真的看不下去了
阅读量:4033 次
发布时间:2019-05-24

本文共 4621 字,大约阅读时间需要 15 分钟。

一:背景

1. 讲故事

前几天看公司一个新项目的底层使用了dapper,大家都知道dapper是一个非常强大的半自动化orm,帮程序员解决了繁琐的mapping问题,用起来非常爽,但我还是遇到了一件非常不爽的事情,如下代码所示:

public class UserDAL : BaseDAL    {        public List
GetList() { using (SqlConnection conn = new SqlConnection(ConnectionString)) { var list = conn.Query
("select * from users").ToList(); return list; } } public bool Insert() { using (SqlConnection conn = new SqlConnection(ConnectionString)) { var execnum = conn.Execute("insert into xxx "); return execnum > 0; } } public bool Update() { using (SqlConnection conn = new SqlConnection(ConnectionString)) { var execnum = conn.Execute("update xxx ...."); return execnum > 0; } } } public class UserModel {}

扫一下代码是不是总感觉哪里不对劲,是的,为了能使用上Dapper的扩展方法,这里面每个方法中都配上了模板化的 using (SqlConnection conn = new SqlConnection(ConnectionString)),虽然这样写逻辑上没有任何问题,但我有洁癖哈,接下来试着封装一下,嘿嘿,用更少的代码做更多的事情。

二:模板化代码封装探索

1. 将模板化的代码提取到父类中

仔细看上面的模板代码你会发现,真正的业务逻辑是写在 using 中的,而该块中只需要拿到一个 conn 就可以了,其他的统一提取封装到父类中,这就可以用到 委托函数啦,对不对,用这个思路代码修改如下:

public class BaseDAL    {        protected string ConnectionString { get; set; }        public T Execute
(Func
func) { using (SqlConnection connection = new SqlConnection(ConnectionString)) { return func(connection); } } }

有了父类通用的 Execute 方法,接下来子类中就可以直接用它啦,改造如下:

public class UserDAL : BaseDAL    {        public List
GetList() { return Execute((conn) => { var list = conn.Query
("select * from users").ToList(); return list; }); } public bool Insert() { return Execute((conn) => { var execnum = conn.Execute("insert into xxx "); return execnum > 0; }); } public bool Update() { return Execute((conn) => { var execnum = conn.Execute("update xxx ...."); return execnum > 0; }); } }

改造之后代码是不是清晰多了,仅仅这一个通用方法貌似还不行,起码 ConnectionString 不能框死。

2. 增加ConnectionString 入口参数

相信有不少朋友的公司是做 ToB 的业务,一般是一个商家一个DB的设计思路,这里就需要在 Execute 上增加一个 ConnectionString 字符串参数,你可以通过重载方法 或者 可选参数,改造如下:

public T Execute
(Func
func) { return Execute(ConnectionString, func); } public T Execute
(string connectionString, Func
func) { using (SqlConnection connection = new SqlConnection(connectionString ?? ConnectionString)) { return func(connection); } } public class UserDAL : BaseDAL { public List
GetList(string connectionString) { return Execute(connectionString, (conn) => { var list = conn.Query
("select * from users").ToList(); return list; }); } }

这样看起来就舒服多了,不过还有一个问题,我们的程序是给客户独立部署的,越简单越好,否则实施人员会砍人的,所以很多用户操作和api轨迹行为都记录到了sqlserver中,这里就有一个 业务表 和 一个 事务日志表,而且要作为原子化提交,这里就涉及到了事务操作。

2. 支持事务操作

因为有同时插入两张表的业务逻辑,免不了使用 transaction,接下来继续扩展 Execute 方法,代码如下:

public T Execute
(Func
func) { return Execute(ConnectionString, func); } public T Execute
(string connectionString, Func
func) { using (SqlConnection connection = new SqlConnection(connectionString ?? ConnectionString)) { connection.Open(); using (var transaction = connection.BeginTransaction()) { return func(connection, transaction); } } }

上面的代码应该很好理解,将 transaction 作为回调函数的参数,业务逻辑部分直接将 transaction 塞入到各自的业务代码中即可,子类可以改造如下:

public bool Insert()        {            return Execute((conn, trans) =>            {                var execnum = conn.Execute("insert into xxx ", transaction: trans);                if (execnum == 0) return false;                var execnum2 = conn.Execute("update xxx set xxx", transaction: trans);                if (execnum2 > 0) trans.Commit();                return execnum > 0;            });        }

这样 Execute 对 transaction 的支持貌似也差不多了,异步版的我就不在此封装啦。

四:总结

文章来源于工作中的点点滴滴,这也是我的即兴封装,大家要是有更好的封装代码,欢迎交流,独乐乐不如众乐乐,本篇就说到这里啦,希望对您有帮助。

转载地址:http://zozdi.baihongyu.com/

你可能感兴趣的文章
慢慢欣赏linux 网络协议栈七 虚拟网卡
查看>>
linux设备驱动模型代码分析
查看>>
uboot入门学习二 位置无关代码以及地址
查看>>
慢慢欣赏linux 块设备驱动基础
查看>>
慢慢欣赏linux文件缓冲区 mmap分析
查看>>
慢慢欣赏linux 页面回收
查看>>
微视linux waitX的意义
查看>>
微视linux 挂接中断action的前奏 设置中断处理函数
查看>>
RCU锁学习
查看>>
ppc解析保留内存
查看>>
微视linux uboot保留内存的传递
查看>>
微视linux scsi驱动错误中断处理
查看>>
linux动态库学习
查看>>
微视linux 释放文件节点流程
查看>>
new的过程
查看>>
作用域、作用域链、自由变量、变量提升
查看>>
var let const 区别
查看>>
事件委托(事件代理)
查看>>
函数防抖和函数节流
查看>>
vue中diff算法
查看>>