.NET Core實戰項目之CMS 第十一章 開發篇-數據庫生成及實體代碼生成器開發

上篇給你們從零開始搭建了一個咱們的ASP.NET Core CMS系統的開發框架,具體爲何那樣設計我也已經在第十篇文章中進行了說明。不過文章發佈後不少人都說了這樣的分層不是很合理,什麼數據庫實體應該跟倉儲放在一塊兒造成領域對象,什麼ViewModel應該放在應用層結構倉儲層與UI層。其實我想說的是,這樣都沒問題,看你本身的理解了!我上篇文章已經說了,若是你願意,徹底能夠把全部的層融合在一塊兒,隨意合併分離這個依你我的喜愛。
我也是本着簡單原則以及合適原則的思想來進行那樣的分層結構,以爲這樣層次更分明些。還有雖然如今DDD的思想很流行,可是實現起來確很複雜,小項目就別那樣折騰了。若是你有不一樣的意見,歡迎加羣討論。什麼?你問我羣號?本身找去,我纔不會告訴你!html

本文已收錄至《.NET Core實戰項目之CMS 第一章 入門篇-開篇及整體規劃
做者:依樂祝
原文地址:https://www.cnblogs.com/yilezhu/p/10112406.htmlmysql

寫在前面

今天咱們就進入.NET Core實戰項目之CMS的開發篇了,在開始以前呢咱們首先須要把咱們前面設計的邏輯模型轉換成對應的物理模型,再根據咱們物理模型生成相應的數據庫腳本,接着咱們就新建數據庫,而後執行下咱們生成的腳本便可。固然這麼多表若是一個一個的寫對應的數據庫實體模型,一個一個的寫倉儲層代碼以及服務層代碼,感受就是在搬磚啊,有木有,因此固然得本身實現個代碼生成器來自動生成這些代碼了!下面咱們一步步來先生成下數據庫而後再打造一個實體模型的代碼生成器吧!git

數據庫生成

生成物理模型

  1. 首先用pdm打開咱們設計的邏輯模型文件,後綴名是ldm的文件,以下圖所示:程序員

    1544535967072

  2. 依次點擊「Tools」-》"Generate Physical Data Model",以下圖所示。或者直接使用快捷鍵Ctrl+Shift+P 打開物理模型生成選項對話框。github

    1544536138034

  3. 以下圖所示選擇號對應的數據庫後,自定義物理模型的名稱代碼後點擊肯定便可生成物理模型。這裏數據庫類型有不少選擇如:mysql,sqlserver,oracle等等,咱們選擇sqlserver2008,你能夠隨意從下拉框選擇一個數據庫進行生成(固然要跟你的數據庫對應)!sql

    1544536437133

  4. 注意這裏生成的物理模型默認是不會生成註釋的以下圖所示:數據庫

    1544537349509

    怎麼辦呢?如何才能講Name 列的內容拷貝到Comment這個裏面呢?由於這個Commment裏面的內容纔會真正的轉換到數據庫字段的註釋。c#

    這時候你須要Tools->Execute Commands->Edit/Run Scripts (或者快捷鍵Ctrl+Shift+X)打開腳本執行的窗口,而後把下面的代碼拷貝進行 run一下便可。oracle

    '代碼一:將Name中的字符COPY至Comment中
    
    
    Option   Explicit 
    ValidationMode   =   True 
    InteractiveMode   =   im_Batch
    
    Dim   mdl   '   the   current   model
    
    '   get   the   current   active   model 
    Set   mdl   =   ActiveModel 
    If   (mdl   Is   Nothing)   Then 
          MsgBox   "There   is   no   current   Model " 
    ElseIf   Not   mdl.IsKindOf(PdPDM.cls_Model)   Then 
          MsgBox   "The   current   model   is   not   an   Physical   Data   model. " 
    Else 
          ProcessFolder   mdl 
    End   If
    
    '   This   routine   copy   name   into   comment   for   each   table,   each   column   and   each   view 
    '   of   the   current   folder 
    Private   sub   ProcessFolder(folder) 
          Dim   Tab   'running     table 
          for   each   Tab   in   folder.tables 
                if   not   tab.isShortcut   then 
                      tab.comment   =   tab.name 
                      Dim   col   '   running   column 
                      for   each   col   in   tab.columns 
                            col.comment=   col.name 
                      next 
                end   if 
          next
    
          Dim   view   'running   view 
          for   each   view   in   folder.Views 
                if   not   view.isShortcut   then 
                      view.comment   =   view.name 
                end   if 
          next
    
          '   go   into   the   sub-packages 
          Dim   f   '   running   folder 
          For   Each   f   In   folder.Packages 
                if   not   f.IsShortcut   then 
                      ProcessFolder   f 
                end   if 
          Next 
    end   sub

    1544537692272

    這裏腳本執行的很快,你也能夠把腳本保存起來下次再用,執行後的效果以下所示:框架

    1544537771197

    咱們的Comment這一行的內容已經跟Name同樣了!

數據庫腳本生成

  1. 首先打開咱們生成的物理模型,擴展名爲pdm的文件,以下圖所示,乍一看跟邏輯模型差很少,實際上仍是有區別的!

    1544538222229

  2. 而後依次以下圖所示選擇「Database」->"Generate Database" 或者快捷鍵Ctrl+G打開數據庫生成選項對話框

    1544538320805

  3. 以下圖所示設置一下生成的數據庫腳本的路徑以及腳本名稱便可生成數據庫腳本文件,以下圖所示:

    1544539197457

  4. 到咱們上面設置的文件夾裏便可查看到咱們生成的數據庫腳本,以下圖所示:

    1544539359326

數據庫生成

  1. 打開咱們的數據庫,並新建一個名爲CzarCms的新的數據庫,以下圖所示:

    1544539477339

  2. 選擇咱們新建的數據庫,而後按照以下圖所示的方式打開咱們剛纔生成的數據庫腳本

    1544539621124

  3. 以下圖所示確認一下目前選擇的是你剛新建的數據庫,而後點擊執行,執行下腳本

    1544539734388

  4. 不出之外的話會出現以下圖所示的「命令已成功完成」的消息,這表示腳本執行成功了,而後刷新下咱們剛纔的數據庫,能夠看到咱們的表已經生成成功了!

    1544539898998

  5. 這裏你能夠檢查下,看看生成的數據庫表有沒有問題,若是有問題的話,從新走一遍流程生成腳本而後執行下就好了,不過須要注意的是,若是你數據庫中有數據就要小心了,從新生成的腳本會drop掉你的表從新建立,因此若是是個別字段出問題的話就邏輯模型以及物理模型修改後,手動在數據庫中修改便可!

  6. 這裏我給每一個表的主鍵設計了自增,給isdelete等等設置了默認的0,以及addtime設置了getdate()等等。

實體模型生成器編寫

好了,上面我已經帶着你一步一步的演示了數據庫的建立過程,下面我就帶着你實現一個簡單的POCO實體對象的代碼生成器吧!什麼?市面上不是有不少代碼生成器嗎?靠,我就是要帶着你本身實現一個,咋滴?是用別人的爽,仍是用本身實現的爽呢?本身琢磨吧!

思考

你們先腦補一下,若是是你想根據數據庫實現一個代碼生成器你的思路是怎樣的呢?是否是首先得獲取下數據庫裏面的全部的表,而後獲取這些表對應的列以及列的類型,是否爲空等等信息。而後再建一個模板,循環這些表的信息來根據模板建立對應的文件呢。
至於模板文件可能你會想到T4或者CodeSmish模板等等,可這些都太複雜了,複雜的語法以及靈活性的問題我這裏選擇另外一個文本文件的形式來進行代碼的生成。

這個代碼生成器的靈感以及部分代碼來自於Zxw.Framework.NetCore,這個框架的github地址是:https://github.com/VictorTzeng/Zxw.Framework.NetCore/tree/master/Zxw.Framework.NetCore 有興趣的小夥伴能夠看下。

下面就讓咱們簡單實現下咱們本身的實體模型代碼生成器吧.

實體代碼生成器

  1. 首先咱們建立一個Option對象來接收咱們所須要的參數,好比說:數據庫類型,數據庫鏈接字符串,做者,實體模型的命名空間等等,以下所示:

    /// <summary>
        /// yilezhu
        /// 2018.12.12
        /// 代碼生成選項
        /// </summary>
        public class CodeGenerateOption
        {
            /// <summary>
            /// 數據庫鏈接字符串
            /// </summary>
            public string ConnectionString { get; set; }
            /// <summary>
            /// 數據庫類型
            /// </summary>
            public string DbType  { get; set; }
            /// <summary>
            /// 做者
            /// </summary>
            public string Author { get; set; }
    
            /// <summary>
            /// 代碼生成時間
            /// </summary>
            public string GeneratorTime { get; set; } = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
    
            /// <summary>
            /// 輸出路徑
            /// </summary>
            public string OutputPath { get; set; }
    
            /// <summary>
            /// 實體命名空間
            /// </summary>
            public string ModelsNamespace { get; set; }
        }
  2. 從數據庫裏面獲取全部表的腳本,這裏我只是簡單的實現了下SqlServer的代碼,後續我會對這塊進行提取封裝,並支持MySql,Oracle,PSQL等等:

    //TODO 從數據庫獲取表列表以及生成實體對象
                if (_options.DbType != DatabaseType.SqlServer.ToString())
                    throw new ArgumentNullException("這是個人錯,目前只支持MSSQL數據庫的代碼生成!後續更新MySQL");
                DatabaseType dbType = DatabaseType.SqlServer;
                string strGetAllTables = @"SELECT DISTINCT d.name as TableName, f.value as TableComment
    FROM      sys.syscolumns AS a LEFT OUTER JOIN
                    sys.systypes AS b ON a.xusertype = b.xusertype INNER JOIN
                    sys.sysobjects AS d ON a.id = d.id AND d.xtype = 'U' AND d.name <> 'dtproperties' LEFT OUTER JOIN
                    sys.syscomments AS e ON a.cdefault = e.id LEFT OUTER JOIN
                    sys.extended_properties AS g ON a.id = g.major_id AND a.colid = g.minor_id LEFT OUTER JOIN
                    sys.extended_properties AS f ON d.id = f.major_id AND f.minor_id = 0";
                List<DbTable> tables = null;
                using (var conn = new SqlConnection(_options.ConnectionString))
                {
                    tables = conn.Query<DbTable>(strGetAllTables).ToList();
  3. 遍歷每一個表而後獲取每一個表對應的列(也是隻實現的SqlServer的代碼)

    tables.ForEach(item =>
                    {
                        string strGetTableColumns = @"SELECT   a.name AS ColName, CONVERT(bit, (CASE WHEN COLUMNPROPERTY(a.id, a.name, 'IsIdentity') 
                    = 1 THEN 1 ELSE 0 END)) AS IsIdentity, CONVERT(bit, (CASE WHEN
                        (SELECT   COUNT(*)
                         FROM      sysobjects
                         WHERE   (name IN
                                             (SELECT   name
                                              FROM      sysindexes
                                              WHERE   (id = a.id) AND (indid IN
                                                                  (SELECT   indid
                                                                   FROM      sysindexkeys
                                                                   WHERE   (id = a.id) AND (colid IN
                                                                                       (SELECT   colid
                                                                                        FROM      syscolumns
                                                                                        WHERE   (id = a.id) AND (name = a.name))))))) AND (xtype = 'PK')) 
                    > 0 THEN 1 ELSE 0 END)) AS IsPrimaryKey, b.name AS ColumnType, COLUMNPROPERTY(a.id, a.name, 'PRECISION') 
                    AS ColumnLength, CONVERT(bit, (CASE WHEN a.isnullable = 1 THEN 1 ELSE 0 END)) AS IsNullable, ISNULL(e.text, '') 
                    AS DefaultValue, ISNULL(g.value, ' ') AS Comment
    FROM      sys.syscolumns AS a LEFT OUTER JOIN
                    sys.systypes AS b ON a.xtype = b.xusertype INNER JOIN
                    sys.sysobjects AS d ON a.id = d.id AND d.xtype = 'U' AND d.name <> 'dtproperties' LEFT OUTER JOIN
                    sys.syscomments AS e ON a.cdefault = e.id LEFT OUTER JOIN
                    sys.extended_properties AS g ON a.id = g.major_id AND a.colid = g.minor_id LEFT OUTER JOIN
                    sys.extended_properties AS f ON d.id = f.class AND f.minor_id = 0
    WHERE   (b.name IS NOT NULL) AND (d.name = @TableName)
    ORDER BY a.id, a.colorder";
                        item.Columns = conn.Query<DbTableColumn>(strGetTableColumns, new
                        {
                            TableName = item.TableName
                        }).ToList();
  4. 接下來就是對數據庫獲取的列進行一個轉換,根據數據庫字段類型轉換成對應的C#類型了

    item.Columns.ForEach(x =>
                        {
                            var csharpType = DbColumnTypeCollection.DbColumnDataTypes.FirstOrDefault(t =>
                                t.DatabaseType == dbType && t.ColumnTypes.Split(',').Any(p =>
                                    p.Trim().Equals(x.ColumnType, StringComparison.OrdinalIgnoreCase)))?.CSharpType;
                            if (string.IsNullOrEmpty(csharpType))
                            {
                                throw new SqlTypeException($"未從字典中找到\"{x.ColumnType}\"對應的C#數據類型,請更新DbColumnTypeCollection類型映射字典。");
                            }
    
                            x.CSharpType = csharpType;
                        });
  5. 既然全部的表以及表對應的列咱們都拿到了,那麼咱們就能夠進行代碼的生成了,固然在生成以前還得建立咱們的模板文件:

    // 本代碼由代碼生成器生成請勿隨意改動
    // 生成時間  {GeneratorTime}
    using System;
    
    namespace {ModelsNamespace}
    {
     /// <summary>
     /// {Author}
     /// {GeneratorTime}
     /// {Comment}
     /// </summary>
     public class {ModelName}
     {
         {ModelProperties}
     }
    }

    看到沒有,很簡單的POCO對象的樣子,接下來就是生成對應的模板了,具體怎麼生成呢?思考下:是否是首先讀取模板文件到一個string裏面,而後就是簡單的replace了!很簡單吧,具體的代碼我都上傳到了Github上,文章末尾我會給出地址。另外爲了你們引用的方便我已經把這個Czar.Cms.Core項目製做成了Nuget包,你們只須要搜索這個包引用下就能夠用了!什麼?Nuget包怎麼引用啊?騷年你能夠上天了~~~~~

測試實體代碼生成器

  1. Czar.Cms.Test 這個項目添加Nuget包引用,引用後的Nuget以下所示:

    1544663123867

  2. 接下來就是新建一個測試類,而後建立一個依賴注入容器,並把咱們須要的Option傳遞進去,以下所示:

    /// <summary>
            /// 構造依賴注入容器,而後傳入參數
            /// </summary>
            /// <returns></returns>
            public IServiceProvider BuildServiceForSqlServer()
            {
                var services = new ServiceCollection();
    
                services.Configure<CodeGenerateOption>(options =>
                {
                    options.ConnectionString = "Data Source=.;Initial Catalog=CzarCms;User ID=sa;Password=1;Persist Security Info=True;Max Pool Size=50;Min Pool Size=0;Connection Lifetime=300;";//這個必須
                    options.DbType = DatabaseType.SqlServer.ToString();//數據庫類型是SqlServer,其餘數據類型參照枚舉DatabaseType//這個也必須
                    options.Author = "yilezhu";//做者名稱,隨你,不寫爲空
                    options.OutputPath = @"E:\workspace\vs2017\Czar.Cms\src\Czar.Cms.Models";//實體模型輸出路徑,爲空則默認爲當前程序運行的路徑
                    options.ModelsNamespace = "Czar.Cms.Models";//實體命名空間
                });
                services.AddSingleton<CodeGenerator>();//注入Model代碼生成器
                return services.BuildServiceProvider(); //構建服務提供程序
            }
  3. 接着就是寫咱們的測試方法了,代碼以下:

    [Fact]
            public void GeneratorModelForSqlServer()
            {
                var serviceProvider= BuildServiceForSqlServer();
                var codeGenerator = serviceProvider.GetRequiredService<CodeGenerator>();
                codeGenerator.GenerateModelCodesFromDatabase();
                Assert.Equal(0,0);
    
            }
  4. 運行一下咱們的Live Unit Testing 而後看一下咱們的Czar.Cms.Models下面已經生成了對應的實體文件,以下圖所示:

    1544663523474

  5. 隨便打開一個看小效果以下:我標註的你猜猜看都是對應的哪一個Options

    1544663584296

開源地址

這個系列教程的源碼我會開放在GitHub以及碼雲上,有興趣的朋友能夠下載查看!以爲不錯的歡迎Star
GitHub:https://github.com/yilezhu/Czar.Cms
碼雲:https://gitee.com/yilezhu/Czar.Cms
若是你以爲這個系列對您有所幫助的話,歡迎以各類方式進行贊助,固然給個Star支持下也是能夠滴!另一種最簡單粗暴的方式就是下面這種直接關注咱們的公衆號了:

第一時間收到更新推送。

總結

這篇文章咱們一步一步的生成了咱們的數據庫,而後手把手帶着你實現了咱們本身的實體模型代碼生成器來簡化咱們的開發過程。接下來咱們就開始實現倉儲層應用層的代碼了,同時咱們會提取通用部分的代碼來進行模板代碼生成來簡化咱們的工做!俗話說的好,不會偷懶的程序員不是一個好爸爸,好丈夫,好兒子,減小代碼的時間多抽點時間陪陪家人吧!若是你有其餘想法能夠在下方留言,或者加羣637326624跟大夥一塊兒討論。共同進步!共勉!

相關文章
相關標籤/搜索