FreeSql.Generator命令行代碼生成器是如何實(shí)現(xiàn)的

目錄

  • FreeSql介紹
  • FreeSql.Generator
  • RazorEngine.NetCore
  • 源碼解析
  • FreeSql.Tools

FreeSql

FreeSql 是功能強(qiáng)大的對象關(guān)系映射技術(shù)(O/RM),支持 .NETCore 2.1+ 或 .NETFramework 4.0+ 或 Xamarin。

有一個強(qiáng)大的ORM,也方便我們開發(fā)一個代碼生成器。

一般情況下,我們開發(fā)數(shù)據(jù)庫相關(guān)的應(yīng)用,主要分為三種code first、db first、model first

我只用過前二種,

  • code first,代碼優(yōu)先,數(shù)據(jù)庫都是根據(jù)實(shí)體類生成,所有的關(guān)系,可以是邏輯關(guān)聯(lián),也可以是物理關(guān)聯(lián)。
  • DB First: 數(shù)據(jù)庫優(yōu)先,直接設(shè)計(jì)表結(jié)構(gòu),用設(shè)計(jì)工具生成表,設(shè)計(jì)主鍵,外鍵、索引,關(guān)聯(lián)關(guān)系等。

當(dāng)我們使用DB First時(shí),設(shè)計(jì)好的數(shù)據(jù)庫,我們怎么生成這些實(shí)體類、通用的代碼、控制器、服務(wù)層、Dto呢。今天我來給大家介紹一下FreeSql項(xiàng)目中的一些工具。當(dāng)然,不使用此ORM的小伙伴也能使用此工具,因?yàn)樗峭ㄓ谩?/p>

FreeSql.Generator 命令行方式

通過幾行命令,就可實(shí)現(xiàn)生成項(xiàng)目中通用的代碼結(jié)構(gòu),不需要復(fù)制一段代碼后修改,加快開發(fā)速度,減少重復(fù)勞動,少用一根頭發(fā)。

由于每個人的項(xiàng)目結(jié)構(gòu),代碼位置各不相同,對于ORM來說,不同的業(yè)務(wù)邏輯各不相同,所以該項(xiàng)目沒有相應(yīng)的模板,相信使用過Razor的同學(xué)一定能實(shí)現(xiàn)自己的模板。

1-2年前,我和一個學(xué)長也寫過代碼生成器,這里分享一下當(dāng)時(shí)做項(xiàng)目時(shí)的一些模板,https://github.com/i542873057/SJNScaffolding/tree/master/SJNScaffolding.RazorPage/Templates,該項(xiàng)目是基于 . NET Core+Razor Page,由于已離職,所以沒有繼續(xù)維護(hù),這些模板都和ABP相關(guān),當(dāng)時(shí)提取了一些通用的功能,單表操作,可以直接生成前后端功能,只需要在word中按統(tǒng)一的格式寫好數(shù)據(jù)字典的文檔,直接復(fù)制到系統(tǒng),即可根據(jù)空格,定義類型等方式解析字段。

回到FreeSql.Generator 命令行

怎么使用呢。

  1. 安裝 dotnet-tool 生成實(shí)體類
dotnet tool install -g FreeSql.Generator
  1. 新建目錄,在地址欄輸入 cmd 快速打開命令窗口,輸入命令:
FreeSql.Generator --help

我們可以看到

C:\Users\igeekfan\Desktop\code>FreeSql.Generator --help
        ____                   ____         __
       / __/  ____ ___  ___   / __/ ___ _  / /
      / _/   / __// -_)/ -_) _\ \  / _ `/ / /
     /_/    /_/   \__/ \__/ /___/  \_, / /_/
                                    /_/


  # Github # https://github.com/2881099/FreeSql v1.5.0

    使用 FreeSql 快速生成數(shù)據(jù)庫的實(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個布爾值,分別對應(yīng):
                               # 首字母大寫
                               # 首字母大寫,其他小寫
                               # 全部小寫
                               # 下劃線轉(zhuǎn)駝峰

     -NameSpace                * 命名空間

     -DB "MySql,Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=數(shù)據(jù)庫;Charset=utf8;SslMode=none;Max pool size=2"

     -DB "SqlServer,Data Source=.;Integrated Security=True;Initial Catalog=數(shù)據(jù)庫;Pooling=true;Max Pool Size=2"

     -DB "PostgreSQL,Host=192.168.164.10;Port=5432;Username=postgres;Password=123456;Database=數(shù)據(jù)庫;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 是國產(chǎn)達(dá)夢數(shù)據(jù)庫

     -Filter                   Table+View+StoreProcedure
                               默認(rèn)生成:表+視圖+存儲過程
                               如果不想生成視圖和存儲過程 -Filter View+StoreProcedure

     -Match                    正則表達(dá)式,只生成匹配的表,如:dbo\.TB_.+

     -FileName                 文件名,默認(rèn):{name}.cs

     -Output                   保存路徑,默認(rèn)為當(dāng)前 shell 所在目錄
                               推薦在實(shí)體類目錄創(chuàng)建 gen.bat,雙擊它重新所有實(shí)體類
  • 更新命令行
dotnet tool update -g FreeSql.Generator
  1. 這里lin-cms-dotnetcore這個項(xiàng)目來測試。

[圖片上傳失敗...(image-1ba3d6-1592919891606)]

[圖片上傳失敗...(image-45671c-1592919891606)]

  • 數(shù)據(jù)庫表名是下劃線,字段也是下劃線方式。
  • -Razor 指定 第一個模板
  • -NameOptions 0,0,0,1 最后一個1,代表 下劃線轉(zhuǎn)駝峰,滿足C#命名規(guī)則
  • -NameSpace 指定了命名空間 LinCms.Core.Entities
  • -DB 就是數(shù)據(jù)庫的相關(guān)配置
  • mysql 本地地址 127.0.0.1 3306端口 用戶名 root 密碼123456 數(shù)據(jù)庫 lin-cms
  • -Match book 這樣就能只生成book,支持正則表達(dá)式,如 -Math lin_user 就會生成以lin_user開頭的表。如dbo.TB_.+,會生成以TB開頭的表。即只生成匹配的表
  1. 執(zhí)行此命令。
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"

這時(shí)候代碼已經(jīng)生成了

[圖片上傳失敗...(image-7694d4-1592919891606)]

其中一個代碼 生成如下。這些類是partial ,熟悉C#的同學(xué),應(yīng)該知道,類的定義使用此關(guān)鍵字,我們能在不同的地方為該類擴(kuò)展。以防止重新同步數(shù)據(jù)庫的結(jié)構(gòu)時(shí),丟失改動的字段。

namespace LinCms.Core.Entities {

    [JsonObject(MemberSerialization.OptIn), Table(Name = "book")]
    public partial class Book {

        /// <summary>
        /// 主鍵Id
        /// </summary>
        [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
    }

}

  • 最終效果圖如下

[圖片上傳失敗...(image-4ef04c-1592919891606)]

此時(shí)會生成二個文件
__重新生成.bat,下次重新點(diǎn)擊他就能重新生成實(shí)體類了。

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

我們可以定義自己的模板,以生成符合自已業(yè)務(wù)的的代碼,從而實(shí)現(xiàn)快速開發(fā)。

我們可以看下模板中的文件內(nèi)容,他就是asp.net下的mvc 結(jié)構(gòu)下的razor后端模板渲染,把這個.txt后綴去掉,就很明了了。對于asp.net mvc的razor,我們可以將控制器下方法的值替換掉cshtml中的值。這個過程是有一個類庫在幫我們實(shí)現(xiàn)的,叫RazorEngine,不過那個是.net framework下的實(shí)踐。.NET Framework 下的RazorEngine代碼生成介紹

@using FreeSql.DatabaseModel;@{
var gen = Model as RazorModel;

Func<string, string> GetAttributeString = attr => {
    if (string.IsNullOrEmpty(attr)) return null;
    return string.Concat(", ", attr.Trim('[', ']'));
};
Func<DbColumnInfo, string> GetDefaultValue = col => {
    if (col.CsType == typeof(string)) return " = string.Empty;";
    return "";
};
}
//xxx
namespace @gen.NameSpace {

@if (string.IsNullOrEmpty(gen.table.Comment) == false) {
    @:/// <summary>
    @:/// @gen.table.Comment.Replace("\r\n", "\n").Replace("\n", "\r\n      /// ")
    @:/// </summary>
}
    [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) {
        @:/// <summary>
        @:/// @col.Coment.Replace("\r\n", "\n").Replace("\n", "\r\n     /// ")
        @:/// </summary>
        }
        @:@("[JsonProperty" + GetAttributeString(gen.GetColumnAttribute(col)) + "]")
        @:public @gen.GetCsType(col) @gen.GetCsName(col.Name) { get; set; }@GetDefaultValue(col)
@:
    }
    }
@gen.GetMySqlEnumSetDefine()
}

RazorEngine.NetCore

到了.NET Core時(shí)代,我看了下FreeSql.Generator用的這個類庫RazorEngine.NetCore,實(shí)現(xiàn)動態(tài)操作cshtml,生成需要的文本。

Razor Engine是基于微軟Razor解析的模板引擎,它允許你使用Razor語法構(gòu)建動態(tài)模板,你只需要使用Engine的靜態(tài)方法,Engine.Razor.RunCompile等。

創(chuàng)建一個控制臺應(yīng)用,然后安裝包。

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);
  • 輸出如下內(nèi)容
Hello World, welcome to RazorEngine!

此處使用的RunCompile方法是擴(kuò)展方法,您需要引用RazorEngine.Templating命名空間。

The "templateKey" 保持唯一值,比如使用guid值。字符串,并且你可以根據(jù)此字符串key重新運(yùn)行緩存的模板。

如果再次根據(jù)此key,可使用原本的模板。

var result = Engine.Razor.Run("templateKey", null, new { Name = "Max" });
  • 會輸出如下內(nèi)容
Hello Max, welcome to RazorEngine!

上面中的RunCompile第三個參數(shù),傳null,因?yàn)槲覀兊谒膫€參數(shù)使用的是匿名類,

根目錄創(chuàng)建一個HelloWord.cshtml,要選擇屬性,->如果較新則復(fù)制 內(nèi)容,

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!
  • 使用強(qiáng)類型 CopyRightUserInfo.cs生成一個版權(quán)所有
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; }
    }

}

根目錄創(chuàng)建一個CopyRightTemplate.cshtml,要選擇屬性,->如果較新則復(fù)制 內(nèi)容,

@{
    var gen = Model as OvOv.Razor.CopyRightUserInfo;
}
//=============================================================
// 創(chuàng)建人:            @gen.UserName
// 創(chuàng)建時(shí)間:          @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 = "710277267@qq.com",
    UserName = "IGeekFan"
});
Console.WriteLine(copyRightResult);

Console.ReadKey();
  • 控制臺輸出
//=============================================================
// 創(chuàng)建人:            IGeekFan
// 創(chuàng)建時(shí)間:          2020/6/23 18:14:08
// 郵箱:             710277267@qq.com
//==============================================================

全放到控制臺下,輸出如下結(jié)果。代碼生成器最重要的一點(diǎn)解決了,我們就能實(shí)現(xiàn)自己的代碼生成器,先構(gòu)建自己的模板,實(shí)現(xiàn)輸入(命令行,WPF,WEB端及更多),輸出(生成文件)。

[圖片上傳失敗...(image-bb9903-1592919891606)]

源碼解析

首先這是一個控制臺應(yīng)用,Main(string[] args)可接收多個參數(shù)。

  1. 處理無參數(shù),--help
  2. 處理args數(shù)組,解析出所有的參數(shù),如果沒有設(shè)置,則為默認(rèn)值。(處理一些參數(shù)異常問題)最重要的是根據(jù)-Razor,選定對應(yīng)的模板。
ArgsRazor=""http://根據(jù)-Razor  1 不是2 還是模板的路徑,取出的模板文本值。
var razorId = Guid.NewGuid().ToString("N");
RazorEngine.Engine.Razor.Compile(ArgsRazor, razorId);
  1. 根據(jù)數(shù)據(jù)庫連接串,取出參數(shù)過濾后的表,視圖,存儲過程
  2. 循環(huán)數(shù)據(jù)庫的表等,
  • model為模板中需要的數(shù)據(jù)
  • razorId與上文的razorId相同,
  • sw為生成后的文本保存的值。
var sw = new StringWriter();
var model = new RazorModel(fsql, ArgsNameSpace, ArgsNameOptions, tables, table);
RazorEngine.Engine.Razor.Run(razorId, sw, null, model);
  1. 將sw字符串保存生成類.cs文件(根據(jù)參數(shù)配置生成文件名)
  2. 另外生成一個__重新生成.bat,__razor.cshtml.txt,方便后續(xù)用戶重新生成實(shí)體類。

FreeSql.Tools

這是 FreeSql 衍生出來的輔助工具包,內(nèi)含生成器等功能;作者:mypeng1985
因?yàn)檫@個不兼容mac,linux,所以作者建議使用dotnet-tool 命令行工具生成實(shí)體類,從而支持MAC/Linux系統(tǒng)。對于不是使用FreeSql的開發(fā)者,也能使用此工具,你只需要修改對應(yīng)的模板即可。

使用方式:不多介紹。

預(yù)覽圖
0.png
0 (1).png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,250評論 6 530
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 97,923評論 3 413
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,041評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,475評論 1 308
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,253評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,801評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,882評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,023評論 0 285
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,530評論 1 331
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,494評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,639評論 1 366
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,177評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,890評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,289評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,552評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,242評論 3 389
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,626評論 2 370