七年前,也就是2007年,我在博客园写了一篇博文,开源并发布了恐怕是我第一个开源项目,DbUtility。其设计的初衷就是为了简化ADO.NET繁琐的数据库访问过程,提供极为简洁流畅的语法访问数据库,像这样:
dbUtility.ExecuteSingleRow("SELECT username, userdata FROM Users WHERE ID = {0}", userId );
这一行代码将自动创建和打开数据库链接、创建命令对象、包装参数、执行查询并将结果包装成DataRow返回。
时过境迁,七年后的现在,我们有了非常多的重量级的ORM如NHibernate、EntityFramework,和各种快速访问数据库的框架。但有些时候,我们仍然会希望,执行一个简单的SQL查询,并获得其结果。
这时候我经常会想起DbUtility这个家伙,所以在七年后的今天,我把DbUtility的所有代码重新写了一次,推出了这个超轻量级数据库访问帮助器的最新版本,DbUtility v3,目前在GitHub上开源。
如果你从来没有听说过这个东西,也没有关系,DbUtility是被设计为超轻量级(无需任何额外的配置,不会产生任何未知的行为),随手可用(语法简单、直接、明了)的数据库访问帮助器。
一个典型的DbUtility v3的代码像是下面这样:
db.T( "SELECT Username FROM Users WHERE ID = {0};", userId ).ExecuteScalar<string>();
几乎每个数据库的查询差不多都是这种形式,其中可以分为三个部分:
查询执行器,即上面代码中的 db ,这个东西负责执行查询,其与数据库直接相关,一般我们可以使用一个连接字符串配置或者连接字符串来创建查询执行器的实例:
var db = new SqlDbUtility( "数据库连接字符串" ); var db = SqlDbUtility.Create( "连接字符串名称" );
紧跟着查询执行器的部分是查询构建器,这一部分决定了数据库具体要执行的查询。也就是上面代码中的 .T( "SELECT Username FROM Users WHERE ID = {0};", userId ) 部分,T的意思是Template,即模板,这也是DbUtility最基本的查询构建方式,通过SQL指令模板来构建。我们可以使用类似于string.Format的语法指定字符串模板和模板参数。但与直接调用string.Format不同的是,这个模板中的参数会被转换为参数化查询中的参数,从而没有注入式漏洞的隐患。即上述的查询模板最终转换成的SQL大体上是这样的:
DECLARE @Param0 AS int = userId-value; SELECT Username FROM Users WHERE ID =@Param0;
T或者Template方法(事实上T是缩写)会解析查询模板并创建一个抽象的参数化查询对象,参数化查询对象会根据具体的数据库产生相应的参数化查询进行执行。
最后的一部分是结果构建器,也就是上面代码中的 .ExecuteScalar<string>() ,ExecuteScalar即是取出查询结果中的第一行第一列。
DbUtility v3提供了极为丰富的结果构建器,除了ExecuteScalar、ExecuteNonQuery、ExecuteDataTable、ExecuteFirstRow这些常见常用的之外,还有自动将结果包装成实体的ExecuteEntity和ExecuteEntities,包装成动态对象的ExecuteDynamicObject和ExecuteDynamics等等等等。丰富的结果构建器可以极大地简化你访问数据库的代码。
不过最重要的是,DbUtility v3设计了一个便于扩展的架构,所有的查询构建器、结果构建器,全部可以简单地自定义扩展,或者说事实上整个DbUtility提供的所有的查询构建器和查询结果构建器本来就是用扩展方法扩展出来的。
如果你要开启数据库事务,使用DbUtility也非常的方便:
using( var transaction = db.BeginTransaction() ) { transaction.T( "SELECT Username FROM Users WHERE ID = {0}", userId ).ExecuteScalar<string>();//事务对象可以直接当做查询执行器来使用。
//... transaction.Commit();//提交事务,若在离开using块之前没有提交事务,则事务会自动回滚。 }
异步查询数据库也非常的简单:
var username = await db.T("SELECT Username FROM Users WHERE ID = {0};", userId ).ExecuteScalarAsync<string>();//加上Async后缀即是访问异步版本。
Jumony的示例项目全部数据库访问改用DbUtility后,比起EntityFramework来说,不仅配置消失了,性能也更好了(因为DbUtility是超轻量级的),更重要的是,结合MVC 4的异步Action,我们可以非常简单的在ASP.NET中异步访问数据库以获得更大的吞吐量:
public async Task<ActionResult> Index() { return View( "index", await dbUtility.T( "SELECT ID, Title, Completed FROM Tasks" ).ExecuteEntitiesAsync<TodoTask>() ); }
在马上发布的更新中,我们可以简单地把多个参数化查询对象像拼接字符串一样拼接起来:
var query = db.T( "SELECT * FROM Users" ); query += "WHERE" + Db.Join( "AND", Db.T( "Age > {0}", age ), Db.T( "FirstName LIKE '%'+{0}+'%'", name ) ); var result = query.ExecuteEntities<User>();
在未来的版本中,将支持更多的数据库类型(如Excel),更好的查询构建(值得期待的超轻量SQL语法: db.Q( "users.username ?users.userId = @0", userId ) )。
DbUtility v3现在已经可以通过 NuGet 获取。
全线开源项目全部在GitHub以Apache协议开源,DbUtility:超轻量数据访问帮助,Jumony:真正的HTML分析、处理、绑定、视图引擎,WebTest:在ASP.NET环境直接进行单元测试,LogUtility:文本日志记录利器。
以上项目均可以在 NuGet 下载。