用了Dapper之后通篇还是SqlConnection,真的看不下去了
一:背景
1. 讲故事
前几天看公司一个新项目的底层使用了dapper,大家都知道dapper是一个非常强大的半自动化orm,帮程序员解决了繁琐的mapping问题,用起来非常爽,但我还是遇到了一件非常不爽的事情,如下代码所示:
public class UserDAL : BaseDAL
{
public List<UserModel> GetList()
{
using (SqlConnection conn = new SqlConnection(ConnectionString))
{
var list = conn.Query<UserModel>("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<T>(Func<SqlConnection, T> func)
{
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
return func(connection);
}
}
}
有了父类通用的 Execute
方法,接下来子类中就可以直接用它啦,改造如下:
public class UserDAL : BaseDAL
{
public List<UserModel> GetList()
{
return Execute((conn) =>
{
var list = conn.Query<UserModel>("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<T>(Func<SqlConnection, T> func)
{
return Execute(ConnectionString, func);
}
public T Execute<T>(string connectionString, Func<SqlConnection, T> func)
{
using (SqlConnection connection = new SqlConnection(connectionString ?? ConnectionString))
{
return func(connection);
}
}
public class UserDAL : BaseDAL
{
public List<UserModel> GetList(string connectionString)
{
return Execute(connectionString, (conn) =>
{
var list = conn.Query<UserModel>("select * from users").ToList();
return list;
});
}
}
这样看起来就舒服多了,不过还有一个问题,我们的程序是给客户独立部署的,越简单越好,否则实施人员会砍人的,所以很多用户操作和api轨迹行为都记录到了sqlserver中,这里就有一个 业务表
和 一个 事务日志表
,而且要作为原子化提交,这里就涉及到了事务操作。
2. 支持事务操作
因为有同时插入两张表的业务逻辑,免不了使用 transaction,接下来继续扩展 Execute 方法,代码如下:
public T Execute<T>(Func<SqlConnection, SqlTransaction, T> func)
{
return Execute(ConnectionString, func);
}
public T Execute<T>(string connectionString, Func<SqlConnection, SqlTransaction, T> 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 的支持貌似也差不多了,异步版的我就不在此封装啦。
四: 总结
文章来源于工作中的点点滴滴,这也是我的即兴封装,大家要是有更好的封装代码,欢迎交流,独乐乐不如众乐乐,本篇就说到这里啦,希望对您有帮助。
如您有更多问题与我互动,扫描下方进来吧~
相关文章
- 一次 Redis 事务使用不当引发的生产事故
- 前端基础(6) - 入门jQuery编程
- 前端基础(4) - CSS定位
- 前端基础(3) - CSS浮动
- 前端基础(2) - CSS入门
- 前端基础(1) - HTML入门
- 爬虫(15) - Scrapy-Redis分布式爬虫(2) | 实例:分布式爬虫项目
- 爬虫(14) - Scrapy-Redis分布式爬虫(1) | 详解
- 爬虫(13) - 反爬虫(2) | 项目示例-爬取瓜子二手车网站数据
- 爬虫(12) - 反爬虫(1) | 详解
- 爬虫(11) - Scrapy框架(3) | 示例项目抓取并下载网站图片
- 爬虫(10) - Scrapy框架(2) | 伪装爬虫
- 爬虫(9) - Scrapy框架(1) | Scrapy 异步网络爬虫框架
- 爬虫(8) - 可视化爬虫框架Selenium
- 爬虫(7) - 网页数据解析(3) | lxml解析数据
- 爬虫(6) - 网页数据解析(2) | BeautifulSoup4在爬虫中的使用
- 爬虫(5) - 网页数据解析(1) | XPath在爬虫中的应用
- 解决方案:可以ping别人,但是别人不能ping我
- 爬虫(4) - Requests(3) | Requests常用场景方法
- 爬虫(3) - Requests(2) | Requests模块的方法、关键字和响应对象属性总结