.NET ORM FreeSql 第一个正式版本发布 v1.0.0
一、簡(jiǎn)介
FreeSql 是 .NET 平臺(tái)下的對(duì)象關(guān)系映射技術(shù)(O/RM),支持 .NetCore 2.1+ 或 .NetFramework 4.0+ 或 Xamarin。
從 0.0.1 發(fā)布到今歷時(shí)整整一年的迭代更新,現(xiàn)在終于敢發(fā)布第一個(gè)正式版。
本文內(nèi)容從簡(jiǎn),介紹項(xiàng)目的主要功能框架,以及暫時(shí)能想到的可能比較有說(shuō)服力的特性。
二、項(xiàng)目統(tǒng)計(jì)
主倉(cāng)庫(kù)解決方案共計(jì)項(xiàng)目:29個(gè)
單元測(cè)試:3510個(gè)
Code Issues:168個(gè)
文檔Wiki:43個(gè)
Stars:1140
Forks:236
Commits:690次
Nuget主包下載量:86,568次
開源地址:https://github.com/2881099/FreeSql
三、功能結(jié)構(gòu)
支持 CodeFirst 遷移,哪怕使用 Access 數(shù)據(jù)庫(kù)也支持;
支持 DbFirst 從數(shù)據(jù)庫(kù)導(dǎo)入實(shí)體類;
支持 深入的類型映射,比如pgsql的數(shù)組類型;
支持 豐富的表達(dá)式函數(shù),以及靈活的自定義解析;
支持 導(dǎo)航屬性一對(duì)多、多對(duì)多貪婪加載,以及延時(shí)加載;
支持 讀寫分離、分表分庫(kù),租戶設(shè)計(jì),過(guò)濾器,樂(lè)觀鎖,悲觀鎖;
支持 MySql/SqlServer/PostgreSQL/Oracle/Sqlite/達(dá)夢(mèng)數(shù)據(jù)庫(kù)/Access;
四、CodeFirst/DbFirst
一切皆 CodeFirst,所有功能都是由實(shí)體類型,到表操作的過(guò)程。CodeFirst 【自動(dòng)遷移】只需要一行代碼:
using FreeSql;
static IFreeSql fsql = new FreeSqlBuilder()
? ? .UseConnectionString(DataType.Sqlite,?
? ? ? ? @"Data Source=|DataDirectory|\document.db;Pooling=true;Max Pool Size=10")
? ? .UseAutoSyncStructure(true) //自動(dòng)同步實(shí)體結(jié)構(gòu)到數(shù)據(jù)庫(kù)
? ? .Build();
在開發(fā)過(guò)程中,表結(jié)構(gòu)會(huì)自動(dòng)創(chuàng)建、或改變(不丟數(shù)據(jù)),取決于實(shí)體類的變化。
CodeFirst 提供功能豐富的特性ColumnAttribute,定義實(shí)體與表間的映射,并且支持 FluentApi 方式。如果不喜歡 ColumnAttribute 這個(gè)名字,還可以通過(guò) AOP 設(shè)置換為 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 數(shù)據(jù)表先行,許多哥們使用動(dòng)軟、T4模板生成實(shí)體類代碼。自已處理每種數(shù)據(jù)庫(kù)的字段類型,和 csharp 類型對(duì)應(yīng),比較麻煩,各大 ORM 可能還不通用。
我們提供命令行工具生成實(shí)體類,dotnet-tools,對(duì)就是它。。非常好用的工具,沒(méi)有之一。
C:\Users\28810>dotnet tool install -g freesql.generator
可使用以下命令調(diào)用工具: FreeSql.Generator
已成功安裝工具“freesql.generator”(版本“1.0.0”)。
C:\Users\28810>freesql.generator
? ? ? ? ____? ? ? ? ? ? ? ? ? ?____? ? ? ? ?__
? ? ? ?/ __/? ____ ___? ___? ?/ __/ ___ _? / /
? ? ? / _/? ?/ __// -_)/ -_) _\ \? / _ `/ / /
? ? ?/_/? ? /_/? ?\__/ \__/ /___/? \_, / /_/
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /_/
? # Github # https://github.com/2881099/FreeSql v1.0.0
? ? 使用 FreeSql 快速生成數(shù)據(jù)庫(kù)的實(shí)體類
? ? 更新工具:dotnet tool update -g FreeSql.Generator
? # 快速開始 #
? > FreeSql.Generator -Razor 1 -NameOptions 0,0,0,0 -NameSpace MyProject -DB "MySql,Data Source=127.0.0.1;..."
? ? ?-Razor 1? ? ? ? ? ? ? ? ? * 選擇模板:實(shí)體類+特性
? ? ?-Razor 2? ? ? ? ? ? ? ? ? * 選擇模板:實(shí)體類+特性+導(dǎo)航屬性
? ? ?-Razor "d:\diy.cshtml"? ? * 自定義模板文件
? ? ?-NameOptions? ? ? ? ? ? ? * 總共4個(gè)布爾值,分別對(duì)應(yīng):
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?# 首字母大寫
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?# 首字母大寫,其他小寫
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?# 全部小寫
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?# 下劃線轉(zhuǎn)駝峰
? ? ?-NameSpace? ? ? ? ? ? ? ? * 命名空間
? ? ?-DB "MySql,Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=數(shù)據(jù)庫(kù);Charset=utf8;SslMode=none;Max pool size=2"
? ? ?-DB "SqlServer,Data Source=.;Integrated Security=True;Initial Catalog=數(shù)據(jù)庫(kù);Pooling=true;Max Pool Size=2"
? ? ?-DB "PostgreSQL,Host=192.168.164.10;Port=5432;Username=postgres;Password=123456;Database=數(shù)據(jù)庫(kù);Pooling=true;Maximum Pool Size=2"
? ? ?-DB "Oracle,user id=user1;password=123456;data source=//127.0.0.1:1521/XE;Pooling=true;Max Pool Size=2"
? ? ?-DB "OdbcDameng,Driver={DM8 ODBC DRIVER};Server=127.0.0.1:5236;Persist Security Info=False;Trusted_Connection=Yes;UID=USER1;PWD=123456789;Max pool size=2"
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?OdbcDameng 是國(guó)產(chǎn)達(dá)夢(mèng)數(shù)據(jù)庫(kù),需要使用 ODBC 連接
? ? ?-Filter? ? ? ? ? ? ? ? ? ?Table+View+StoreProcedure
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?默認(rèn)生成:表+視圖+存儲(chǔ)過(guò)程
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?如果不想生成視圖和存儲(chǔ)過(guò)程 -Fitler View+StoreProcedure
? ? ?-FileName? ? ? ? ? ? ? ? ?文件名,默認(rèn):{name}.cs
? ? ?-Output? ? ? ? ? ? ? ? ? ?保存路徑,默認(rèn)為當(dāng)前 shell 所在目錄
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?推薦在實(shí)體類目錄創(chuàng)建 gen.bat,雙擊它重新所有實(shí)體類
它基于 Razor 模板生成,支持自定義模板生成,意味著它遠(yuǎn)不止可以生成實(shí)體類,甚至是 IRepository 或者。。。
五、導(dǎo)航屬性
從一開始就著重導(dǎo)航對(duì)象的設(shè)計(jì),支持一對(duì)多、多對(duì)多、父子關(guān)系、一對(duì)一、多對(duì)一,不夸張的說(shuō)目前對(duì)導(dǎo)航屬性處理最流弊,最容易上手的 ORM。在多表查詢的表達(dá)式使用上非常便利,如下:
fsql.Select<Catetory>()
? ? .Where(a => a.Parent.Parent.Name == "粵語(yǔ)")
可以使用導(dǎo)航屬性一直這樣點(diǎn)下去。。。
級(jí)聯(lián)保存,級(jí)聯(lián)查詢功能也必不可少,如下查詢多對(duì)多:
fsql.Select<Song>()
? ? .IncludeMany(a => a.Tags)
? ? .ToList();
上面的代碼,如果只返回 Tags 前 5條記錄,也是支持的 .IncludeMany(a => a.Tags.Take(5))
對(duì)性能有追求,還可以指定 Tags 只查詢部分字段
關(guān)于 IncludeMany 不便再這過(guò)多展開介紹。。。(其實(shí)還有黑科技!)
哦,還有 FreeSql.AdminLTE 擴(kuò)展包,它不屬于主倉(cāng)庫(kù)項(xiàng)目,最大化利用導(dǎo)航屬性完成通用的 CURD 后臺(tái)管理功能。
流弊噠噠~~~~
六、倉(cāng)儲(chǔ)模式
倉(cāng)儲(chǔ)工作單元目前是當(dāng)下的流行風(fēng),在比較早的時(shí)候大約0.2版本發(fā)布了第一個(gè)倉(cāng)儲(chǔ)版本,當(dāng)時(shí)參考了大量的項(xiàng)目設(shè)計(jì),最終選用 abp vnext 的 IRepository 設(shè)計(jì)接口,實(shí)現(xiàn)通用倉(cāng)儲(chǔ)類功能。
也就是說(shuō),使用 FreeSql.Repository 你不必再自己寫那些繁瑣的 CURD 重復(fù)的倉(cāng)儲(chǔ)功能,不用再頭疼倉(cāng)儲(chǔ)類的接口方法定義。定義標(biāo)準(zhǔn)比寫代碼難多了,abp vnext 的 IRepository 目前是見(jiàn)過(guò)最好的,木有之一!!
倉(cāng)儲(chǔ)模式都在操作實(shí)體對(duì)象,無(wú)論是更新還是刪除,都是傳對(duì)象。。。傳傳傳。。。
問(wèn)題1、傳對(duì)象更新,意味著更新所有字段?
不會(huì)的,我們的倉(cāng)儲(chǔ)實(shí)現(xiàn)擁有狀態(tài)管理機(jī)制,從對(duì)象查詢出來(lái)的時(shí)候已經(jīng)記錄了拍照,當(dāng)調(diào)用更新方法的時(shí)候會(huì)與之對(duì)比,計(jì)算出變化的字段,只更新變化的字段!
var repo = fsql.GetRepository<Song>();
var item = repo.Where(a => a.Id == 1).First();
item.Title = "原諒我今天";
repo.Update(item);
提示:支持樂(lè)觀鎖、悲觀鎖
問(wèn)題2、狀態(tài)管理是否影響性能?
不完全,因?yàn)闋顟B(tài)管理設(shè)計(jì)在倉(cāng)儲(chǔ)實(shí)現(xiàn)之上,我們最原始的 IFreeSql 沒(méi)有這個(gè)功能(倉(cāng)儲(chǔ)算是一種擴(kuò)展包吧,但是倉(cāng)儲(chǔ)又非常有效)。倉(cāng)儲(chǔ)即用即銷毀,擅用它的對(duì)比功能更新對(duì)象,不濫用沒(méi)有性能問(wèn)題。
有了倉(cāng)儲(chǔ)怎么會(huì)沒(méi)有 UnitOfWork 呢,UnitOfWork 目前以事務(wù)的方式做了默認(rèn)實(shí)現(xiàn),并且它擁有實(shí)體變化跟蹤記錄。
七、性能
1、插入測(cè)試(52個(gè)字段)
測(cè)試結(jié)果,是在相同操作系統(tǒng)下進(jìn)行的,并且都有預(yù)熱
18W 解釋:插入18萬(wàn)行記錄,表格中的數(shù)字是執(zhí)行時(shí)間(單位ms)
Oracle 插入性能不用懷疑,可能安裝學(xué)生版限制較大
提醒:開源數(shù)據(jù)庫(kù)測(cè)試結(jié)果比較有意義,商業(yè)數(shù)據(jù)庫(kù)版本之間性能可能有較大差距
2、插入測(cè)試(10個(gè)字段)
?
提示:已經(jīng)支持了 SqlServer 數(shù)據(jù)庫(kù)的 SqlBulkCopy 功能、以及 PostgreSQL 數(shù)據(jù)庫(kù)的 Copy 功能
八、拉姆達(dá)
非常特色的功能之一,深入細(xì)化函數(shù)解析,所支持的類型基本都可以使用對(duì)應(yīng)的表達(dá)式函數(shù),例如 日期、字符串、IN查詢、數(shù)組(PostgreSQL的數(shù)組)、字典(PostgreSQL HStore)等等。
1、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))
已優(yōu)化,防止 where in 元素多過(guò)的 SQL 錯(cuò)誤,如:
[Err] ORA-01795: maximum number of expressions in a list a 1000
原來(lái):where id in (1…1333)
現(xiàn)在:where id in (1…500) or id in (501…1000) or id in (1001…1333)
2、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')
3、自定義函數(shù)
默認(rèn)已經(jīng)支持了很豐富的函數(shù)解析,如果不夠再自己定義:
[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) //重寫內(nèi)容
? ? ? ? ? ? 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 已兼容表達(dá)式解析,分別解析為:N’’ 和 ‘’,優(yōu)化索引執(zhí)行計(jì)劃
九、騷操作
1、代碼注釋 -> 遷移到數(shù)據(jù)庫(kù)
CodeFirst 支持將 c# 代碼內(nèi)的注釋,遷移至數(shù)據(jù)庫(kù)的備注。先決條件:
實(shí)體類所在程序集,需要開啟 xml 文檔功能;
xml 文件必須與程序集同目錄,且文件名:xxx.dll -> xxx.xml;
2、NoneParameter
可以設(shè)置不使用 參數(shù)化 執(zhí)行 SQL 命令,方便開發(fā)調(diào)試,區(qū)別如下:
INSERT INTO `tb_topic`(`Title`) VALUES(?Title0)
INSERT INTO `tb_topic`(`Title`) VALUES('Title_1')
在 new FreeSqlBuilder().UseNoneParameter(true) 全局設(shè)置
在 單次 ISelect、IInsert、IDelete、IUpdate 上使用 NoneParameter() 設(shè)置單次生效
3、Dto 映射查詢
用過(guò) ProjectTo 功能嗎?沒(méi)用過(guò)當(dāng)忽略此行。。。
有些朋友可能是先 ToList().Mapper<T>(),這樣會(huì)先查詢了所有字段。
Dto 映射查詢支持單表/多表,這個(gè)功能可以決定只查詢部分字段(不是、不是、不是先查詢所有字段再到內(nèi)存映射)。
規(guī)則:查找屬性名,會(huì)循環(huán)內(nèi)部對(duì)象 _tables(多表會(huì)增長(zhǎng)),以 主表優(yōu)先查,直到查到相同的字段。
如: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<匿名對(duì)象>
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>
4、WhereCascade
FreeSql 擅長(zhǎng)多表查詢,遇到像isdeleted每個(gè)表都給條件的時(shí)候,挺麻煩。WhereCascade使用后生成sql時(shí),所有表都附上這個(gè)條件。
如:
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
其中的實(shí)體可附加表達(dá)式時(shí)才生效,支持子表查詢。單次查詢使用的表數(shù)目越多收益越大。
5、審計(jì) CURD
如果因?yàn)槟硞€(gè) sql 騷操作耗時(shí)很高,沒(méi)有一個(gè)相關(guān)的審計(jì)功能,排查起來(lái)可以說(shuō)無(wú)從下手。
FreeSql 支持簡(jiǎn)單的類似功能:
fsql.Aop.CurdAfter = (s, e) => {
if (e.ElapsedMilliseconds > 200) {
//記錄日志
//發(fā)送短信給負(fù)責(zé)人
}
};
只需要一個(gè)事件,就可以對(duì)全局起到作用。
還有一個(gè) CurdBefore 在執(zhí)行 sql 之前觸發(fā),常用于記錄日志或開發(fā)調(diào)試。
6、審計(jì)屬性值
實(shí)現(xiàn)插入/更新時(shí)統(tǒng)一處理某些值,比如某屬性的雪花算法值、創(chuàng)建時(shí)間值、甚至是業(yè)務(wù)值。
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; }
? ? //...
}
當(dāng)屬性的類型是 long,并且標(biāo)記了 [Snowflake],并且當(dāng)前值是 0,那么在插入/更新時(shí)它的值將設(shè)置為雪花id值。
說(shuō)明:SnowflakeAttribute 是使用者您來(lái)定義,new Snowflake().GetId() 也是由使用者您來(lái)實(shí)現(xiàn)
如果命名規(guī)范,可以在 aop 里判斷,if (e.Property.Name == “createtime”) e.Value = DateTime.Now;
還有。。還有很多騷操作。。不便在此展開。。。
十、展望 2020
2019 年支持了主流的數(shù)據(jù)庫(kù):
SqlServer 2000-2019,支持 row_number/offset fetch next 分頁(yè)自動(dòng)版本選擇適配,以及其他語(yǔ)法的差異適配,提供 ado.net 與 odbc 兩種實(shí)現(xiàn)方式;
PostgreSQL 9.4-12,完成了版本間部分差異適配,提供 ado.net 與 odbc 兩種實(shí)現(xiàn)方式;
MySql 5.5、Mariadb,提供 Oracle 官方驅(qū)動(dòng)、與 MySqlConnector 社區(qū)驅(qū)動(dòng),還有 odbc 實(shí)現(xiàn)方式;
Oracle 11+,提供 ado.net 與 odbc 兩種實(shí)現(xiàn)方式;
Sqlite,兼容了 .net core / .net framework / xamarin 平臺(tái)適配,支持 CodeFirst 開發(fā)模式,一個(gè)字爽!!!
MsAccess 2003-2007,提供 oledb 實(shí)現(xiàn)方式,支持 CodeFirst 開發(fā)模式;
達(dá)夢(mèng),提供 odbc 的實(shí)現(xiàn)方式,并且支持 DbFirst 和 CodeFirst 兩種開發(fā)模式;
2020 年支持國(guó)產(chǎn)是重點(diǎn),重心,重要的工作內(nèi)容,南大通用將是下一個(gè)目標(biāo),并且已經(jīng)在進(jìn)行中了。
開源地址:https://github.com/2881099/FreeSql
寫到最后面,感謝這一年來(lái)與 FreeSql 一直陪伴的兄弟朋友們。
原文鏈接:https://blog.csdn.net/dotnetCore/article/details/103704647
總結(jié)
以上是生活随笔為你收集整理的.NET ORM FreeSql 第一个正式版本发布 v1.0.0的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 基于 Kubernetes 的微服务部署
- 下一篇: AspNetCore结合Redis实践消