1. 程式人生 > >FreeSql.Generator命令列程式碼生成器是如何實現的

FreeSql.Generator命令列程式碼生成器是如何實現的

## 目錄 - FreeSql介紹 - FreeSql.Generator - RazorEngine.NetCore - 原始碼解析 - FreeSql.Tools ## FreeSql FreeSql 是功能強大的物件關係對映技術(O/RM),支援 .NETCore 2.1+ 或 .NETFramework 4.0+ 或 Xamarin。 有一個強大的ORM,也方便我們開發一個程式碼生成器。 一般情況下,我們開發資料庫相關的應用,主要分為三種code first、db first、model first 我只用過前二種, - code first,程式碼優先,資料庫都是根據實體類生成,所有的關係,可以是邏輯關聯,也可以是物理關聯。 - DB First: 資料庫優先,直接設計表結構,用設計工具生成表,設計主鍵,外來鍵、索引,關聯關係等。 當我們使用DB First時,設計好的資料庫,我們怎麼生成一些實體類、通用的程式碼、控制器、服務層、Dto呢。今天我來給大家介紹一下FreeSql專案中的一些工具。當然,不使用此ORM的小夥伴也能使用此工具,因為他是通用。 ## FreeSql.Generator 命令列方式 通過幾行命令,就可實現生成專案中通用的程式碼結構,不需要複製一段程式碼後修改,加快開發速度,減少重複勞動,少用一根頭髮。 由於每個人的專案結構,程式碼位置各不相同,對於ORM來說,不同的業務邏輯各不相同,所以該專案沒有相應的模板,相信使用過Razor的同學一定能實現自己的模板。 1-2年前,我和一個學長也寫過程式碼生成器,這裡分享一下當時做專案時的一些模板,[https://github.com/i542873057/SJNScaffolding/tree/master/SJNScaffolding.RazorPage/Templates](https://github.com/i542873057/SJNScaffolding/tree/master/SJNScaffolding.RazorPage/Templates),該專案是基於 . NET Core+Razor Page,由於已離職,所以沒有繼續維護,這些模板都和ABP相關,當時提取了一些通用的功能,單表操作,可以直接生成前後端功能,只需要在word中按統一的格式寫好資料字典的文件,直接複製到系統,即可根據空格,定義型別等方式解析欄位。 **回到FreeSql.Generator 命令列** - 對於此工具的使用可參考 [https://github.com/dotnetcore/FreeSql/wiki/DbFirst](https://github.com/dotnetcore/FreeSql/wiki/DbFirst) - 原始碼位置 [https://github.com/dotnetcore/FreeSql/tree/master/Extensions/FreeSql.Generator](https://github.com/dotnetcore/FreeSql/tree/master/Extensions/FreeSql.Generator) - 前提是本地安裝了.net core 3.1 的sdk. 怎麼使用呢。 1. 安裝 dotnet-tool 生成實體類 ``` dotnet tool install -g FreeSql.Generator ``` 2. 新建目錄,在位址列輸入 cmd 快速開啟命令視窗,輸入命令: ``` FreeSql.Generator --help ``` 我們可以看到 ``` C:\Users\igeekfan\Desktop\code>FreeSql.Generator --help ____ ____ __ / __/ ____ ___ ___ / __/ ___ _ / / / _/ / __// -_)/ -_) _\ \ / _ `/ / / /_/ /_/ \__/ \__/ /___/ \_, / /_/ /_/ # Github # https://github.com/2881099/FreeSql v1.5.0 使用 FreeSql 快速生成資料庫的實體類 更新工具: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 * 選擇模板:實體類+特性 -Razor 2 * 選擇模板:實體類+特性+導航屬性 -Razor "d:\diy.cshtml" * 自定義模板檔案 -NameOptions * 總共4個布林值,分別對應: # 首字母大寫 # 首字母大寫,其他小寫 # 全部小寫 # 下劃線轉駝峰 -NameSpace * 名稱空間 -DB "MySql,Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=資料庫;Charset=utf8;SslMode=none;Max pool size=2" -DB "SqlServer,Data Source=.;Integrated Security=True;Initial Catalog=資料庫;Pooling=true;Max Pool Size=2" -DB "PostgreSQL,Host=192.168.164.10;Port=5432;Username=postgres;Password=123456;Database=資料庫;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 "Sqlite,Data Source=document.db" -DB "Dameng,server=127.0.0.1;port=5236;user id=2user;password=123456789;database=2user;poolsize=2" Dameng 是國產達夢資料庫 -Filter Table+View+StoreProcedure 預設生成:表+檢視+儲存過程 如果不想生成檢視和儲存過程 -Filter View+StoreProcedure -Match 正則表示式,只生成匹配的表,如:dbo\.TB_.+ -FileName 檔名,預設:{name}.cs -Output 儲存路徑,預設為當前 shell 所在目錄 推薦在實體類目錄建立 gen.bat,雙擊它重新所有實體類 ``` - 更新命令列 ``` dotnet tool update -g FreeSql.Generator ``` 3. 這裡lin-cms-dotnetcore這個專案來測試。 ![https://pic.downk.cc/item/5ef1c55614195aa5940207cf.jpg](https://pic.downk.cc/item/5ef1c55614195aa5940207cf.jpg) ![](https://pic.downk.cc/item/5ef1c57914195aa594022b44.jpg) - 資料庫表名是下劃線,欄位也是下劃線方式。 - -Razor 指定 第一個模板 - -NameOptions 0,0,0,1 最後一個1,代表 下劃線轉駝峰,滿足C#命名規則 - -NameSpace 指定了名稱空間 LinCms.Core.Entities - -DB 就是資料庫的相關配置 - mysql 本地地址 127.0.0.1 3306埠 使用者名稱 root 密碼123456 資料庫 lin-cms - -Match book 這樣就能只生成book,支援正則表示式,如 -Math lin_user 就會生成以lin_user開頭的表。如dbo\.TB_.+,會生成以TB開頭的表。即只生成匹配的表 4. 執行此命令。 ``` FreeSql.Generator -Razor 1 -NameOptions 0,0,0,1 -NameSpace LinCms.Core.Entities -DB "MySql,Data Source=127.0.0.1;Port=3306;User ID=root;Password=123456;Initial Catalog=lincms;Charset=utf8;SslMode=none;Max pool size=2" ``` 這時候程式碼已經生成了 ![](https://pic.downk.cc/item/5ef1c60f14195aa59402b271.jpg) 其中一個程式碼 生成如下。這些類是partial ,熟悉C#的同學,應該知道,類的定義使用此關鍵字,我們能在不同的地方為該類擴充套件。以防止重新同步資料庫的結構時,丟失改動的欄位。 ``` namespace LinCms.Core.Entities { [JsonObject(MemberSerialization.OptIn), Table(Name = "book")] public partial class Book { /// /// 主鍵Id ///
[JsonProperty, Column(Name = "id", IsPrimary = true, IsIdentity = true)] public long Id { get; set; } [JsonProperty, Column(Name = "author", DbType = "varchar(20)")] public string Author { get; set; } = string.Empty; [JsonProperty, Column(Name = "image", DbType = "varchar(50)")] public string Image { get; set; } = string.Empty; //更多xxx } } ``` - 最終效果圖如下 ![](https://pic.downk.cc/item/5ef1c74614195aa59403b019.jpg) 此時會生成二個檔案 __重新生成.bat,下次重新點選他就能重新生成實體類了。 ``` FreeSql.Generator -Razor "__razor.cshtml.txt" -NameOptions 1,1,0,1 -NameSpace MyProject -DB "MySql,Data Source=127.0.0.1;Port=3306;User ID=root;Password=123456;Initial Catalog=lincms;Charset=utf8;SslMode=none;Max pool size=2" -FileName "{name}.cs" ``` 上面的命令-Razor 指定了這個txt檔案 __razor.cshtml.txt 我們可以定義自己的模板,以生成符合自已業務的的程式碼,從而實現快速開發。 我們可以看下模板中的檔案內容,他就是asp.net下的mvc 結構下的razor後端模板渲染,把這個.txt字尾去掉,就很明瞭了。對於asp.net mvc的razor,我們可以將控制器下方法的值替換掉cshtml中的值。這個過程是有一個類庫在幫我們實現的,叫RazorEngine,不過那個是.net framework下的實踐。[.NET Framework 下的RazorEngine程式碼生成介紹](https://blog.csdn.net/q710777720/article/details/91358617) ```csharp @using FreeSql.DatabaseModel;@{ var gen = Model as RazorModel; Func GetAttributeString = attr => { if (string.IsNullOrEmpty(attr)) return null; return string.Concat(", ", attr.Trim('[', ']')); }; Func GetDefaultValue = col => { if (col.CsType == typeof(string)) return " = string.Empty;"; return ""; }; } //xxx namespace @gen.NameSpace { @if (string.IsNullOrEmpty(gen.table.Comment) == false) { @:/// @:/// @gen.table.Comment.Replace("\r\n", "\n").Replace("\n", "\r\n /// ") @:///
} [JsonObject(MemberSerialization.OptIn)@GetAttributeString(gen.GetTableAttribute())] public partial class @gen.GetCsName(gen.FullTableName) { @foreach (var col in gen.columns) { if (string.IsNullOrEmpty(col.Coment) == false) { @:/// @:/// @col.Coment.Replace("\r\n", "\n").Replace("\n", "\r\n /// ") @:///
} @:@("[JsonProperty" + GetAttributeString(gen.GetColumnAttribute(col)) + "]") @:public @gen.GetCsType(col) @gen.GetCsName(col.Name) { get; set; }@GetDefaultValue(col) @: } } @gen.GetMySqlEnumSetDefine() } ``` ## RazorEngine.NetCore 到了.NET Core時代,我看了下FreeSql.Generator用的這個類庫[RazorEngine.NetCore](https://github.com/fouadmess/RazorEngine),實現動態操作cshtml,生成需要的文字。 Razor Engine是基於微軟Razor解析的模板引擎,它允許你使用Razor語法構建動態模板,你只需要使用Engine的靜態方法,Engine.Razor.RunCompile等。 建立一個控制檯應用,然後安裝包。 ``` Install-Package RazorEngine.NetCore ``` ``` using RazorEngine; using RazorEngine.Templating; // For extension methods. string template = "Hello @Model.Name, welcome to RazorEngine!"; var result = Engine.Razor.RunCompile(template, "templateKey", null, new { Name = "World" }); Console.WriteLine(result); ``` - 輸出如下內容 ``` Hello World, welcome to RazorEngine! ``` > 此處使用的**RunCompile**方法是擴充套件方法,您需要引用RazorEngine.Templating名稱空間。 The "templateKey" 保持唯一值,比如使用guid值。字串,並且你可以根據此字串key重新執行快取的模板。 如果再次根據此key,可使用原本的模板。 ``` var result = Engine.Razor.Run("templateKey", null, new { Name = "Max" }); ``` - 會輸出如下內容 ``` Hello Max, welcome to RazorEngine! ``` 上面中的RunCompile第三個引數,傳null,因為我們第四個引數使用的是匿名類, 根目錄建立一個HelloWord.cshtml,要選擇屬性,->如果較新則複製 內容, ``` Hello @Model.Name, welcome to RazorEngine! ``` 控制檯如下程式碼。 ``` string templateFilePath = "HelloWorld.cshtml"; var templateFile = File.ReadAllText(templateFilePath); string templateFileResult = Engine.Razor.RunCompile(templateFile, Guid.NewGuid().ToString(), null, new { Name = "World" }); Console.WriteLine(templateFileResult); ``` - 控制檯輸出 ``` Hello World, welcome to RazorEngine! ``` - 使用強型別 CopyRightUserInfo.cs生成一個版權所有 ``` using System; namespace OvOv.Razor { public class CopyRightUserInfo { public string UserName { get; set; } public string EmailAddress { get; set; } public DateTime CreateTime { get; set; } public string FileRemark { get; set; } } } ``` 根目錄建立一個CopyRightTemplate.cshtml,要選擇屬性,->如果較新則複製 內容, ``` @{ var gen = Model as OvOv.Razor.CopyRightUserInfo; } //============================================================= // 建立人: @gen.UserName // 建立時間: @gen.CreateTime // 郵箱: @gen.EmailAddress //============================================================== ``` 控制檯如下程式碼。 ``` string copyRightTemplatePath = "CopyRightTemplate.cshtml"; var copyRightTemplate = File.ReadAllText(copyRightTemplatePath); string copyRightResult = Engine.Razor.RunCompile(copyRightTemplate, Guid.NewGuid().ToString(), typeof(CopyRightUserInfo), new CopyRightUserInfo { CreateTime = DateTime.Now, EmailAddress = "[email protected]", UserName = "IGeekFan" }); Console.WriteLine(copyRightResult); Console.ReadKey(); ``` - 控制檯輸出 ``` //============================================================= // 建立人: IGeekFan // 建立時間: 2020/6/23 18:14:08 // 郵箱: [email protected] //============================================================== ``` 全放到控制檯下,輸出如下結果。程式碼生成器最重要的一點解決了,我們就能實現自己的程式碼生成器,先構建自己的模板,實現輸入(命令列,WPF,WEB端及更多),輸出(生成檔案)。 ![](https://pic.downk.cc/item/5ef1d60914195aa5940ff447.jpg) - 以上原始碼已放到示例程式碼中 [https://github.com/luoyunchong/dotnetcore-examples/blob/master/aspnetcore-freesql/OvOv.Razor/Program.cs](https://github.com/luoyunchong/dotnetcore-examples/blob/master/aspnetcore-freesql/OvOv.Razor/Program.cs) ## 原始碼解析 首先這是一個控制檯應用,Main(string[] args)可接收多個引數。 1. 處理無引數,--help 2. 處理args陣列,解析出所有的引數,如果沒有設定,則為預設值。(處理一些引數異常問題)最重要的是根據-Razor,選定對應的模板。 ``` ArgsRazor=""//根據-Razor 1 不是2 還是模板的路徑,取出的模板文字值。 var razorId = Guid.NewGuid().ToString("N"); RazorEngine.Engine.Razor.Compile(ArgsRazor, razorId); ``` 3. 根據資料庫連線串,取出引數過濾後的表,檢視,儲存過程 4. 迴圈資料庫的表等, - model為模板中需要的資料 - razorId與上文的razorId相同, - sw為生成後的文字儲存的值。 ``` var sw = new StringWriter(); var model = new RazorModel(fsql, ArgsNameSpace, ArgsNameOptions, tables, table); RazorEngine.Engine.Razor.Run(razorId, sw, null, model); ``` 5. 將sw字串儲存生成類.cs檔案(根據引數配置生成檔名) 6. 另外生成一個__重新生成.bat,__razor.cshtml.txt,方便後續使用者重新生成實體類。 ## FreeSql.Tools 這是 FreeSql 衍生出來的輔助工具包,內含生成器等功能;作者:mypeng1985 因為這個不相容mac,linux,所以作者建議使用dotnet-tool 命令列工具生成實體類,從而支援MAC/Linux系統。對於不是使用FreeSql的開發者,也能使用此工具,你只需要修改對應的模板即可。 使用方式:不多介紹。 - [https://github.com/2881099/Freesql.tools](https://github.com/2881099/Freesql.tools) - 分為WPF ,WinForm + DSkin 版本(套網頁) - 看了下程式碼,底層生成程式碼邏輯也是用的RazorEngine [.NET Framework 下的RazorEngine程式碼生成介紹](https://blog.csdn.net/q710777720/article/details/91358617) ##### 預覽圖 ![](https://pic.downk.cc/item/5ef1e12314195aa59418d82e.png) ![](https://pic.downk.cc/item/5ef1e11a14195aa59418cfb9