[開源] FreeSql 配套工具,基於 Razor 模板實現最高相容的生成器
FreeSql 經過半年的開發和堅持維護,在 0.6.x 版本中完成了幾大重要事件:
1、按小包拆分,每個資料庫實現為單獨 dll;
2、實現 .net framework 4.5 支援;
3、同時支援 MySql.Data、MySqlConnector 的實現;
4、自定義導航屬性關係的配置;
5、配套工具 FreeSql.Tools 釋出;
本文主要講解第5項《FreeSql.Tools》,大主角往往在最後才出現!!!
拆分小包
在此之前一直被吐槽 FreeSql 臃腫,沒有小包開發理念。其實我是一點也不承認這種評價,雖然剛開始只有一個 FreeSql.dll,但是在開發和規劃上簡單了很多。
有一條開發原則這樣講道:過早優化是惡夢!
大概意思是無論做什麼專案,不要想著一開始就過度系統的、規範的執行。從外界來看是正規了,但是進度和穩定性會大大折扣。可以不信我,但是請一定要相信前人的總結啊!!!
從之前的一個 dll 到拆分成小包,我們總共耗時兩天,雖然都在一個專案內開發,但其實耦合性並不高,so easy!!
車到山前必有路,時機到了自然會拆。這個時機也是奠定 FreeSql 走出了穩定關鍵的一步。這樣會有更多人願意加入 FreeSql 陣營。
- 各資料庫單獨包、延時載入包;
- FreeSql.Extensions.LazyLoading
- FreeSql.Provider.MySql
- FreeSql.Provider.PostgreSQL
- FreeSql.Provider.SqlServer
- FreeSql.Provider.Sqlite
- FreeSql.Provider.Oracle
支援 .netframework 4.5
早期 FreeSql 主要是在 .net core 最方便的 ORM!NETStandard 是新的標準,然而前段時間微軟又說 ..net5 將合併。。。變化真的太快。
在實現拆分小包後,其實 FreeSql 的模組更加清淅,並且依賴項非常之少,然後比較容易的做出了 4.5 framework 的適配。
目前支援的版本:
Package Name | Version |
---|---|
FreeSql.Provider.MySql | NETStandard2.0、net452 |
FreeSql.Provider.PostgreSQL | NETStandard2.0、net45 |
FreeSql.Provider.SqlServer | NETStandard2.0、net451 |
FreeSql.Provider.Sqlite | NETStandard2.0、net45 |
FreeSql.Provider.Oracle | NETStandard2.0、net45 |
FreeSql.Extensions.LazyLoading | NETStandard2.0、net45 |
MySqlConnector 的實現
mysql 是一個神奇的流行資料庫,在 .net 陣營中使用量排名老二。mysql 的版本五花八門,從 5.6 開始有了不同的分支,分支的出現使得 ado.net 驅動不通用。
很多人不推薦使用 MySql.Data 官方驅動,但是 FreeSql 一直在使用官驅,並且支援了所有 5.6 型別,包括 enum/set 等。
然後就有一些人,特別是高手的那些來提出要求,適配一個 MySqlConnector 的實現,然後著名的 A大(茶叔)提了一道 PR ,建立了 FreeSql.Provider.MySqlConnector 專案,99.9999% 原始碼和原來 FreeSql.Provider.MySql 相同,經過 266 個單元測試後發現,只需要相容 enum/set 型別,引數化 ? @ 的處理就跑通了。然後就有了現在新的驅動包:
Package Name | Version |
---|---|
FreeSql.Provider.MySqlConnector | NETStandard2.0、net45 |
然後 FreeSqlBuilder 使用反射決定使用哪個 mysql 驅動。程式碼如下:
public IFreeSql<TMark> Build<TMark>() {
if (string.IsNullOrEmpty(_masterConnectionString)) throw new Exception("引數 masterConnectionString 不可為空,請檢查 UseConnectionString");
IFreeSql<TMark> ret = null;
Type type = null;
switch(_dataType) {
case DataType.MySql:
type = Type.GetType("FreeSql.MySql.MySqlProvider`1,FreeSql.Provider.MySql")?.MakeGenericType(typeof(TMark));
if (type == null) type = Type.GetType("FreeSql.MySql.MySqlProvider`1,FreeSql.Provider.MySqlConnector")?.MakeGenericType(typeof(TMark));
if (type == null) throw new Exception("缺少 FreeSql 資料庫實現包:FreeSql.Provider.MySql.dll,可前往 nuget 下載");
break;
case DataType.SqlServer: type = Type.GetType("FreeSql.SqlServer.SqlServerProvider`1,FreeSql.Provider.SqlServer")?.MakeGenericType(typeof(TMark));
if (type == null) throw new Exception("缺少 FreeSql 資料庫實現包:FreeSql.Provider.SqlServer.dll,可前往 nuget 下載");
break;
case DataType.PostgreSQL: type = Type.GetType("FreeSql.PostgreSQL.PostgreSQLProvider`1,FreeSql.Provider.PostgreSQL")?.MakeGenericType(typeof(TMark));
if (type == null) throw new Exception("缺少 FreeSql 資料庫實現包:FreeSql.Provider.PostgreSQL.dll,可前往 nuget 下載");
break;
case DataType.Oracle: type = Type.GetType("FreeSql.Oracle.OracleProvider`1,FreeSql.Provider.Oracle")?.MakeGenericType(typeof(TMark));
if (type == null) throw new Exception("缺少 FreeSql 資料庫實現包:FreeSql.Provider.Oracle.dll,可前往 nuget 下載");
break;
case DataType.Sqlite: type = Type.GetType("FreeSql.Sqlite.SqliteProvider`1,FreeSql.Provider.Sqlite")?.MakeGenericType(typeof(TMark));
if (type == null) throw new Exception("缺少 FreeSql 資料庫實現包:FreeSql.Provider.Sqlite.dll,可前往 nuget 下載");
break;
default: throw new Exception("未指定 UseConnectionString");
}
ret = Activator.CreateInstance(type, new object[] { _masterConnectionString, _slaveConnectionString }) as IFreeSql<TMark>;
if (ret != null) {
ret.CodeFirst.IsAutoSyncStructure = _isAutoSyncStructure;
ret.CodeFirst.IsSyncStructureToLower = _isSyncStructureToLower;
ret.CodeFirst.IsSyncStructureToUpper = _isSyncStructureToUpper;
ret.CodeFirst.IsConfigEntityFromDbFirst = _isConfigEntityFromDbFirst;
ret.CodeFirst.IsNoneCommandParameter = _isNoneCommandParameter;
ret.CodeFirst.IsLazyLoading = _isLazyLoading;
var ado = ret.Ado as Internal.CommonProvider.AdoProvider;
ado.AopCommandExecuting += _aopCommandExecuting;
ado.AopCommandExecuted += _aopCommandExecuted;
}
return ret;
}
自定義導航屬性關係的配置
FreeSql 原先支援約定式導航關係配置,對於新專案開發無疑可約定,但是很多老專案命名不規範的就使用不了相關的功能。
有關約定配置可參考 github wiki 中心文件
QQ 開發群真是個好平臺,在發起討論後,各位大佬都紛紛提出建議,最後以一票否決了各大建議,哈哈。。
主要從語法和使用者使用的感受上設計,還是那個理念:日式簡約!不能加入太多特性和功能,增加使用者的理解和使用成本。
最終效果如下:
//導航屬性,OneToMany
[Navigate("Song_id")]
public virtual List<song_tag> Obj_song_tag { get; set; }
//導航屬性,ManyToOne/OneToOne
[Navigate("Song_id")]
public virtual song Obj_song { get; set; }
[Navigate("Tag_id")]
public virtual tag Obj_tag { get; set; }
然後就能使用很多導航的騷操作功能了。
配套工具 FreeSql.Tools 釋出(主角壓軸)
在此感謝這個工具的作者:mypeng1985,和參考者:movingsam
感謝有你們一幫熱心的使用者,幫助 FreeSql 生態添磚加瓦!!
FreeSql 在早期做過一套生成器模板,功能比較隱祕,一般人不知道如何使用。。之後就一直沉迷於 CodeFirst 的功能開發,無法自拔。
然後在10天前,突然感覺 FreeSql 多了好多使用者,這個時間當然需要有從資料庫生成實體的需求了!!
Q:沒必要搞這種東西了吧 市面上蠻多的,或者搞一套模板完全搞定了?
A:
無法100%型別相容啊,因為 FreeSql 支援的型別真的很深,然後市場上的型別對映做不到 100% 匹配;
為了挖掘更多功能,生成器還需要有導航屬性的支援,這是基本的,因為有導航屬性後,FreeSql 操作會騷許多;
本來我發起了一個純 winform 的生成器專案,介面都做好了如下:
是不是覺得很好看?我覺得好看。。。。其他人覺得醜。我在開發群發給大家看了之後,第二天 FreeSql.Tools 專案就搞出了新的介面,如下:
直接被秒殺了,這是來自作者:mypeng1985 的佳作。
介面看上去非常像 web,但其實不是的,仍然是一個 winform 程式,使用了 html 做介面,c# 做操作功能。
結束語
原始碼地址:https://github.com/2881099/FreeSql.Tools
FreeSql 從 2018年11月28日立項,開發,到今天 0.6.x,單元測試 1600+,生態也逐漸完善,有得到許多網友的鼓勵和支援,感謝你們!感謝參與專案的你們