衆所周知 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(在線)