FreeSql 是 .NET 平臺下的對象關係映射技術(O/RM),支持 .NetCore 2.1+ 或 .NetFramework 4.0+ 或 Xamarin。git
從 0.0.1 發佈,歷時整整一年的迭代更新,原計劃元旦發佈1.0,可能做者比較急提早了幾天發佈。實際上是元旦有其餘事……github
本文內容從簡,介紹項目的主要功能框架,以及暫時能想到的可能比較有說服力的特性。算法
主倉庫解決方案共計項目:29個sql
單元測試:3510個shell
Code Issues:168個數據庫
文檔Wiki:43個express
Stars:1140c#
Forks:236數組
Commits:690次app
Nuget主包下載量:86,568次
開源地址:https://github.com/2881099/FreeSql
一切皆 CodeFirst,全部功能都是由實體類型,到表操做的過程。CodeFirst 【自動遷移】只須要一行代碼:
using FreeSql; static IFreeSql fsql = new FreeSqlBuilder() .UseConnectionString(DataType.Sqlite, @"Data Source=|DataDirectory|\document.db;Pooling=true;Max Pool Size=10") .UseAutoSyncStructure(true) //自動同步實體結構到數據庫 .Build();
在開發過程當中,表結構會自動建立、或改變(不丟數據),取決於實體類的變化。
CodeFirst 提供功能豐富的特性ColumnAttribute,定義實體與表間的映射,而且支持 FluentApi 方式。若是不喜歡 ColumnAttribute 這個名字,還能夠經過 AOP 設置換爲 MyColumnAttribute。
using FreeSql.DataAnnotations; class Song { [Column(IsIdentity = true)] public int Id { get; set; } public string Title { get; set; } public string Url { get; set; } public DateTime CreateTime { get; set; } }
DbFirst 數據表先行,許多哥們使用動軟、T4模板生成實體類代碼。自已處理每種數據庫的字段類型,和 csharp 類型對應,比較麻煩,各大 ORM 可能還不通用。
咱們提供命令行工具生成實體類,dotnet-tools,對就是它。。很是好用的工具,沒有之一。
C:\Users\28810>dotnet tool install -g freesql.generator 可以使用如下命令調用工具: FreeSql.Generator 已成功安裝工具「freesql.generator」(版本「1.0.0」)。 C:\Users\28810>FreeSql.Generator —help
它基於 Razor 模板生成,支持自定義模板生成,意味着它遠不止能夠生成實體類,甚至是 IRepository 或者。。。
從一開始就着重導航對象的設計,支持一對多、多對多、父子關係、一對1、多對一,不誇張的說目前對導航屬性處理最流弊,最容易上手的 ORM。多表查詢的表達式使用很是便利,以下:
fsql.Select<Catetory>() .Where(a => a.Parent.Parent.Name == "粵語")
可使用導航屬性一直這樣點下去。。。
級聯保存,級聯查詢功能也必不可少,以下查詢多對多:
fsql.Select<Song>() .IncludeMany(a => a.Tags) .ToList();
上面的代碼,若是隻返回 Tags 前 5條記錄,也是支持的 .IncludeMany(a => a.Tags.Take(5))
對性能有追求,還能夠指定 Tags 只查詢部分字段
關於 IncludeMany 不便再這過多展開介紹。。。(其實還有黑科技!)
哦,還有 FreeSql.AdminLTE 擴展包,它不屬於主倉庫項目,最大化利用導航屬性完成通用的 CURD 後臺管理功能。
流弊噠噠~~~~
倉儲工做單元目前是當下的流行風,在比較早的時候大約0.2版本發佈了第一個倉儲版本,當時參考了大量的項目設計,最終選用 abp vnext 的 IRepository 設計接口,實現通用倉儲類功能。
也就是說,使用 FreeSql.Repository 你沒必要再本身寫那些繁瑣的 CURD 重複的倉儲功能,不用再頭疼倉儲類的接口方法定義。定義標準比寫代碼難多了,abp vnext 的 IRepository 目前是見過最好的,木有之一!!
倉儲模式都在操做實體對象,不管是更新仍是刪除,都是傳對象。。。傳傳傳。。。
問題一、傳對象更新,意味着更新全部字段?
不會的,咱們的倉儲實現擁有狀態管理機制,從對象查詢出來的時候已經記錄了拍照,當調用更新方法的時候會與之對比,計算出變化的字段,只更新變化的字段!
var repo = fsql.GetRepository<Song>(); var item = repo.Where(a => a.Id == 1).First(); item.Title = "原諒我今天"; repo.Update(item);
提示:支持樂觀鎖、悲觀鎖
問題二、狀態管理是否影響性能?
不徹底,由於狀態管理設計在倉儲實現之上,咱們最原始的 IFreeSql 沒有這個功能(倉儲算是一種擴展包吧,可是倉儲又很是有效)。倉儲即用即銷燬,擅用它的對比功能更新對象,不濫用沒有性能問題。
有了倉儲怎麼會沒有 UnitOfWork 呢,UnitOfWork 目前以事務的方式作了默認實現,而且它擁有實體變化跟蹤記錄。
一、插入測試(52個字段)
18W | 1W | 5K | 2K | 1K | 500 | 100 | 50 | |
---|---|---|---|---|---|---|---|---|
MySql 5.5 ExecuteAffrows | 55,497 | 4,953 | 2,304 | 2,554 | 1,516 | 1,572 | 265 | 184 |
SqlServer Express ExecuteAffrows | 402,355 | 24,847 | 11,465 | 4,971 | 2,437 | 915 | 138 | 88 |
SqlServer Express ExecuteSqlBulkCopy | 21,065 | 578 | 326 | 139 | 105 | 79 | 60 | 48 |
PostgreSQL 10 ExecuteAffrows | 46,756 | 3,294 | 2,269 | 1,019 | 374 | 209 | 51 | 37 |
PostgreSQL 10 ExecutePgCopy | 10,090 | 583 | 337 | 136 | 88 | 61 | 30 | 25 |
Oracle XE ExecuteAffrows | - | - | - | - | 24,528 | 10,648 | 571 | 200 |
Sqlite ExecuteAffrows | 28,554 | 1,149 | 701 | 327 | 155 | 91 | 44 | 35 |
測試結果,是在相同操做系統下進行的,而且都有預熱
18W 解釋:插入18萬行記錄,表格中的數字是執行時間(單位ms)
Oracle 插入性能不用懷疑,可能安裝學生版限制較大
提醒:開源數據庫測試結果比較有意義,商業數據庫版本之間性能可能有較大差距
二、插入測試(10個字段)
18W | 1W | 5K | 2K | 1K | 500 | 100 | 50 | |
---|---|---|---|---|---|---|---|---|
MySql 5.5 ExecuteAffrows | 15,380 | 1,813 | 1,457 | 1,254 | 563 | 246 | 55 | 21 |
SqlServer Express ExecuteAffrows | 47,204 | 2,275 | 1,108 | 488 | 279 | 123 | 35 | 16 |
SqlServer Express ExecuteSqlBulkCopy | 4,248 | 127 | 71 | 30 | 48 | 14 | 11 | 10 |
PostgreSQL 10 ExecuteAffrows | 9,786 | 568 | 336 | 157 | 102 | 34 | 9 | 6 |
PostgreSQL 10 ExecutePgCopy | 4,081 | 167 | 93 | 39 | 21 | 12 | 4 | 2 |
Oracle XE ExecuteAffrows | - | - | - | - | 2,394 | 731 | 67 | 33 |
Sqlite ExecuteAffrows | 4,524 | 246 | 137 | 94 | 35 | 19 | 14 | 11 |
提示:已經支持了 SqlServer 數據庫的 SqlBulkCopy 功能、以及 PostgreSQL 數據庫的 Copy 功能
很是特點的功能之一,深刻細化函數解析,所支持的類型基本均可以使用對應的表達式函數,例如 日期、字符串、IN查詢、數組(PostgreSQL的數組)、字典(PostgreSQL HStore)等等。
一、In查詢
var t1 = fsql.Select<T>() .Where(a => new[] { 1, 2, 3 }.Contains(a.Id)) .ToSql(); //SELECT .. FROM .. //WHERE (a.`Id` in (1,2,3))
已優化,防止 where in 元素多過的 SQL 錯誤,如:
[Err] ORA-01795: maximum number of expressions in a list a 1000
原來:where id in (1..1333)
如今:where id in (1..500) or id in (501..1000) or id in (1001..1333)
二、In查詢(多列)
//元組集合 vae lst = new List<(Guid, DateTime)>(); lst.Add((Guid.NewGuid(), DateTime.Now)); lst.Add((Guid.NewGuid(), DateTime.Now)); lst.Add((Guid.NewGuid(), DateTime.Now)); fsql.Select<T>() .Where(a => lst.Contains(a.Id, a.ct1)) .ToSql(); //SELECT .. FROM .. //WHERE (a."Id" = '685ee1f6-bdf6-4719-a291-c709b8a1378f' AND a."ct1" = '2019-12-07 23:55:27' OR //a."Id" = '5ecd838a-06a0-4c81-be43-1e77633b7404' AND a."ct1" = '2019-12-07 23:55:27' OR //a."Id" = 'b8b366f3-1c03-4547-9c96-d362dd5cae6a' AND a."ct1" = '2019-12-07 23:55:27')
三、自定義函數
默認已經支持了很豐富的函數解析,若是不夠再本身定義:
[ExpressionCall] public static class DbFunc { //必要定義 static + ThreadLocal static ThreadLocal<ExpressionCallContext> context = new ThreadLocal<ExpressionCallContext>(); public static DateTime FormatDateTime(this DateTime that, string arg1) { var up = context.Value; if (up.DataType == FreeSql.DataType.Sqlite) //重寫內容 context.Value.Result = $"date_format({up.ParsedContent["that"]}, {up.ParsedContent["arg1"]})"; return that; } } fsql.Select<T>().ToSql(a => a.CreateTime.FormatDateTime("yyyy-MM-dd")); //SELECT date_format(a."CreateTime", 'yyyy-MM-dd') as1 //FROM "T" a
提示:SqlServer nvarchar/varchar 已兼容表達式解析,分別解析爲:N'' 和 '',優化索引執行計劃
一、代碼註釋 -> 遷移到數據庫
CodeFirst 支持將 c# 代碼內的註釋,遷移至數據庫的備註。先決條件:
二、NoneParameter
能夠設置不使用 參數化 執行 SQL 命令,方便開發調試,區別以下:
INSERT INTO `tb_topic`(`Title`) VALUES(?Title0) INSERT INTO `tb_topic`(`Title`) VALUES('Title_1')
在 new FreeSqlBuilder().UseNoneParameter(true) 全局設置
在 單次 ISelect、IInsert、IDelete、IUpdate 上使用 NoneParameter() 設置單次生效
三、Dto 映射查詢
用過 ProjectTo 功能嗎?沒用過當忽略此行。。。
有些朋友多是先 ToList().Mapper<T>(),這樣會先查詢了全部字段。
Dto 映射查詢支持單表/多表,這個功能能夠決定只查詢部分字段(不是、不是、不是先查詢全部字段再到內存映射)。
規則:查找屬性名,會循環內部對象 _tables(多表會增加),以 主表優先查,直到查到相同的字段。
如:A, B, C 都有 id,Dto { id, a1, a2, b1, b2 },A.id 被映射。也能夠指定 id = C.id 映射。
fsql.Select<Song>().ToList(a => new DTO { xxx = a.ext }) //狀況1:附加全部映射,再額外映射 ext,返回 List<DTO> fsql.Select<Song>().ToList(a => new Song { id = a.id }) //狀況2:只查詢 id,返回 List<Song> fsql.Select<Song>().ToList(a => new { id = a.id }) //狀況3:只查詢 id,返回 List<匿名對象> fsql.Select<Song>().ToList(a => new DTO(a.id)) //狀況4:只查詢 id,返回 List<DTO> fsql.Select<Song>().ToList(a => new DTO(a.id) { xxx = a.ext }) //狀況5:查詢 id, ext,返回 List<DTO> fsql.Select<Song>().ToList(a => new Song(a.id)) //狀況6:查詢 id,返回 List<Song> fsql.Select<Song>().ToList(a => new Song(a.id) { xxx = a.ext }) //狀況7:查詢 id, ext,返回 List<Song>
四、WhereCascade
FreeSql 擅長多表查詢,遇到像isdeleted每一個表都給條件的時候,挺麻煩。WhereCascade使用後生成sql時,全部表都附上這個條件。
如:
fsql.Select<t1>() .LeftJoin<t2>(...) .WhereCascade(x => x.IsDeleted == false) .ToList();
獲得的 SQL:
SELECT ... FROM t1 LEFT JOIN t2 on ... AND (t2.IsDeleted = 0) WHERE t1.IsDeleted = 0
其中的實體可附加表達式時才生效,支持子表查詢。單次查詢使用的表數目越多收益越大。
五、審計 CURD
若是由於某個 sql 騷操做耗時很高,沒有一個相關的審計功能,排查起來能夠說無從下手。
FreeSql 支持簡單的相似功能:
fsql.Aop.CurdAfter = (s, e) => { if (e.ElapsedMilliseconds > 200) { //記錄日誌 //發送短信給負責人 } };
只須要一個事件,就能夠對全局起到做用。
還有一個 CurdBefore 在執行 sql 以前觸發,經常使用於記錄日誌或開發調試。
六、審計屬性值
實現插入/更新時統一處理某些值,好比某屬性的雪花算法值、建立時間值、甚至是業務值。
fsql.Aop.AuditValue += (s, e) => { if (e.Column.CsType == typeof(long) && e.Property.GetCustomAttribute<SnowflakeAttribute>(false) != null && e.Value?.ToString() == 0) e.Value = new Snowflake().GetId(); }; class Order { [Snowflake] public long Id { get; set; } //... }
當屬性的類型是 long,而且標記了 [Snowflake],而且當前值是 0,那麼在插入/更新時它的值將設置爲雪花id值。
說明:SnowflakeAttribute 是使用者您來定義,new Snowflake().GetId() 也是由使用者您來實現
若是命名規範,能夠在 aop 裏判斷,if (e.Property.Name == "createtime") e.Value = DateTime.Now;
還有。。還有不少騷操做。。不便在此展開。。。
2019 年支持了主流的數據庫:
SqlServer 2000-2019,支持 row_number/offset fetch next 分頁自動版本選擇適配,以及其餘語法的差別適配,提供 ado.net 與 odbc 兩種實現方式;
PostgreSQL 9.4-12,完成了版本間部分差別適配,提供 ado.net 與 odbc 兩種實現方式;
MySql 5.五、Mariadb,提供 Oracle 官方驅動、與 MySqlConnector 社區驅動,還有 odbc 實現方式;
Oracle 11+,提供 ado.net 與 odbc 兩種實現方式;
Sqlite,兼容了 .net core / .net framework / xamarin 平臺適配,支持 CodeFirst 開發模式,一個字爽!!!
MsAccess 2003-2007,提供 oledb 實現方式,支持 CodeFirst 開發模式;
達夢,提供 odbc 的實現方式,而且支持 DbFirst 和 CodeFirst 兩種開發模式;
2020 年支持國產是重點,重心,重要的工做內容,南大通用將是下一個目標,而且已經在進行中了。
開源地址:https://github.com/2881099/FreeSql
寫到最後面,感謝這一年來與 FreeSql 一直陪伴的兄弟朋友們。