众所周知 Dapper 是 .NET 下最轻最快的 ORM,它是喜欢写 SQL 码农的福音,相对于 SqlHelper 它更加方便,据统计 10个 .NETer 有 9个 用过 Dapper。git
因为 .NET 环境的特殊,对 Lambda 表达式树的喜好,因而市面上有不少出现了基于 Dapper 的轻量级 ORM,它们几乎都有共同特色,让 Dapper 支持 Lambda 表达式树,写起来顺畅如流水。github
今天介绍一款自己功能已经很强大的 ORM,他提供一个隐藏得比较深的 API 功能,使用起来和 Dapper 没什么两样。sql
这要先从 Lambda 表达式开始提及,词语中少了一个“树”字,差异甚大。数据库
表达式,以下各类语法糖骚操做,产生的 IL 如出一辙:c#
//使用C# 2.0中的匿名方法获取字符串长度 Func<string, int> strLength = delegate(string str) { return str.Length; }; Console.WriteLine(strLength("Hello World!")); //使用Lambda表达式 //(显式类型参数列表)=> {语句},lambda表达式最冗长版本 strLength = (string str) => { return str.Length; }; Console.WriteLine(strLength("Hello World!")); //单一表达式做为主体 //(显式类型参数列表)=> 表达式 strLength = (string str) => str.Length; Console.WriteLine(strLength("Hello World!")); //隐式类型的参数列表 //(隐式类型参数列表)=> 表达式 strLength = (str) => str.Length; Console.WriteLine(strLength("Hello World!")); //单一参数的快捷语法 //参数名 => 表达式 strLength = str => str.Length; Console.WriteLine(strLength("Hello World!"));
而表达式树呢,代码写起来跟表达式差很少,以下:bash
Expression<Func<string, int>> strLength = str => str.Length;
表达式树不支持代码块(花括号)数据结构
力求书写简单,通常状况咱们都是这样写的,虽然它和表达式代码写起来几乎同样,可是返回值和表达式不同,多了一个泛型 Expression<>。app
表达式树也称表达式目录树,将代码以一种抽象的方式表示成一个对象树,树中每一个节点自己都是一个表达式。表达式树不是可执行代码,它是一种数据结构。它是代码在编译期间将编写的代码转换成一个树结构,以便后续进行逆向解析。ide
如上:(strLength.Body as MemberExpression).Member.Name 能够获得值 "Length"函数
因为表达式树可逆向解析的特色,近十年来 EF 是一直是带头大哥,国产每一年都要整出好几个 ORM,大部分都是基于表达式树解析作的。
.NET 技术文章历来不缺乏表达式树解析的这类文章,有兴趣的能够百度搜索一下,不少不少,可是想作完美可不是件简单事。
FreeSql 在表达式树解析上作了下足了工夫,举例:
一、子表 in 查询
.Where(a => fsql.Select<T>().ToList(b => b.Id).Contains(a.Id)) //WHERE a.Id in (select id from t)
二、子表 exists 查询
.Where(a => fsql.Select<T>().Any(b => b.Id == a.Id)) //WHERE exists(select 1 from t where Id = a.Id)
三、日期格式化
ToList(a => a.CreateTime.ToString("HH:mm:ss")) //date_format(a.`CreateTime`, '%H:%i:%s')
四、开窗函数
ToList(a => SqlExt.Rank().Over().OrderBy(a.Id).OrderByDescending(b.EdiId).ToValue()) //rank() over(order by a.Id, b.EdiId desc)
五、Join 子表
ToList(a => string.Join(",", fsql.Select<StringJoin01>().ToList(b => b.Id))) //(SELECT group_concat(b.`Id` separator ',') FROM `StringJoin01` b)
这些特性在不一样的数据库,都须要作相应适配实现,FreeSql 还支持对导航属性的表达式树解析,说这些只想证实作到细致真的不容易。
与其本身造着麻烦,不如直接拿来主义使用?
与其本身造着麻烦,不如直接拿来主义使用?FreeSql 提供了一种非主打的 API 使用习惯,使用起来跟 Dapper 没什么区别。
支持 MySql/SqlServer/PostgreSQL/Oracle/Sqlite/Firebird/达梦/神通/人大金仓/翰高/MsAccess 十多种数据库适配,支持 Ado.net/Odbc,而且支持 .NetFramework 4.0 平台、.NET5.0、.NET Core2.1 + 平台。
第一步:以数据库 SqlServer 访问为例,只须要安装已经划分好的小包:
dotnet add packages FreeSql.Provider.SqlServer
or
Install-Package FreeSql.Provider.SqlServer
第二步:创建实体类
class TestConnectionExt { public Guid id { get; set; } public string title { get; set; } public DateTime createTime { get; set; } = DateTime.Now; }
第三步:开始 CRUD
using (var conn = new SqlConnection(connectString)) { var list = conn.Select<TestConnectionExt>().Where(a => a.id == item.id).ToList(); } using (var conn = new SqlConnection(connectString)) { var item = new TestConnectionExt { title = "testinsert" }; var affrows = conn.Insert(item).ExecuteAffrows(); } using (var conn = new SqlConnection(connectString)) { var affrows = conn.Update<TestConnectionExt>() .Where(a => a.Id == xxx) .Set(a => a.title, "testupdated") .ExecuteAffrows(); } using (var conn = new SqlConnection(connectString)) { var affrows = conn.Delete<TestConnectionExt>() .Where(a => a.Id == xxx) .ExecuteAffrows(); }
添加或更新:
using (var conn = new SqlConnection(connectString)) { var affrows = conn.InsertOrUpdate<TestConnectionExt>() .SetSource(item) .ExecuteAffrows(); }
如上添加、删除、修改、查询,已经支持实体类操做,而且支持批量插入、批量更新、批量删除、多表查询、导航属性查询。
能够享用 FreeSql 几乎全部功能。
思考:使用这种 API 貌似能够很轻松的接入到 abp vnext 中?
FreeSql 使用世界上最宽松的开源协议 MIT 托管于 github:https://github.com/dotnetcore/FreeSql 目前已发布经历两年高频率迭代的稳定版本 v2.0,欢迎关注和使用。
支持 .NetFramework 4.0+、.NetCore 2.1+、Xamarin 等支持 NetStandard 全部运行平台。
支持 MySql/SqlServer/PostgreSQL/Oracle/Sqlite/Firebird/达梦/神通/人大金仓/翰高/MsAccess 数据库,支持 Ado.net/Odbc。
QQ群:4336577(已满)、8578575(在线)、52508226(在线)