在dbfrist 時代,用T4模板生成程式碼,貌似還沒有感覺到彆扭。但是到了codefrist 後,我想要實體生成生成備註,我就得想方設法的去把備註弄到資料庫,然後 還要處理模型中型別像列舉這種屬性,漸漸的感覺到了吃力。要不換種方式吧,想著去反射實體,但是用T4去處理這種反射,還是感覺到有點吃力,就覺得能不能直接像我們直接寫後臺程式一樣去解析,後面想到了Razor 引擎,經過進一步的瞭解,發現大神封裝了一個元件 RazorEngine.NetCore,他很好的解決了我的問題。
下面就讓我們來了解下這個元件吧,先讓我們得到這個元件如圖
有了元件,我們先去編寫對應自己業務的模板先吧,就比如我這個我建立一個AddDto,我建立的模板如下:
@*@model LazyBoy.Dtos.DbTableDto*@ @using LazyBoy.Dtos; @using LazyBoy.Extensions; @using LazyBoy.Enums; using System; using AutoMapper; using Domain.Models.Enum; using Domain.Models.Entitys; using Sy.ExpressionBuilder.Modules; using System.ComponentModel.DataAnnotations; using Sy.ExpressionBuilder.Modules; using Domain.Models.Entitys; using AutoMapper; using Domain.Models.Enum; namespace @GeneratorConfig.DtoNameSpaceName { /// <summary> /// @Model.Remark ///</summary> [AutoMap(typeof(@(@StringExtension.FirstToUp(@Model.TableName))), ReverseMap = true)] public partial class BaseAdd@(@StringExtension.FirstToUp(@Model.TableName))Dto { @foreach (var pm in @Model.DbColumns) { @if ((pm.ColumnName.ToLower() != "id"&&(pm.PropertyType == EnumPropertyType.Field || pm.PropertyType == EnumPropertyType.Enum) )) { @:/// <summary> @:/// @pm.Remark @:/// </summary> @:[Display(Name ="@(pm.Remark)")] @if (pm.IsRequired) { @:[Required(ErrorMessage ="@(pm.Remark)不能為空")] } @if (pm.StringLengthMax!=0) { @:[StringLength(@(pm.StringLengthMax), MinimumLength =@(pm.StringLengthMin), ErrorMessage = "@(pm.Remark)的長度為{2}至{1}個字元")] } else if (pm.StringLengthMin!=0) { @:[StringLength(@(pm.StringLengthMin), ErrorMessage = "@(pm.Remark)的長度至少為{1}個字元")] } @if(pm.RangeMax!=null) { @:[Range(@(pm.RangeMax), MinimumLength =@(pm.RangeMin), ErrorMessage="@(pm.Remark)的範圍在{1}至{2}之間")] } @if(pm.Regular!=null) { @:[RegularExpression("@(pm.Regular)", ErrorMessage = "@(pm.Remark)@(pm.ErrorMessage)")] } @:public virtual @pm.ColumnType @(pm.IsNullable==true?"?":"") @StringExtension.FirstToUp(pm.ColumnName) { get;set;} } } } }
溫馨提示,程式碼頭部這個引用後臺返回的實體的這個,我們編寫的時候放出來,這樣我們就可以像寫Razor 檢視一樣了,後面生成程式碼的註釋掉。
有了模板後,我們就可以生成我們要的AddDto了,在起始項(.net6 直接在Program) 新增並且編譯我們的模板,如下:
//開啟並且讀取模板 string template = File.ReadAllText(filePath); //CreateDto.cshtml var nameKey = templateName.ToLower().Replace(".cshtml", ""); //新增模板 Engine.Razor.AddTemplate(nameKey, template); //編譯模板 Engine.Razor.Compile(nameKey, null);
然後我們根據反射拿到實體類庫的解析類,傳給引擎就好了
var result = Engine.Razor.Run(templateName.ToLower(), null, item);
templateName.ToLower(),對應我們新增模板的key,item對應單個實體的解析類,下面給出我的示例程式碼:
/// <summary> /// 建立所有型別Dto /// </summary> /// <returns></returns> [HttpGet] public IResultModel CreateAllBaseDto() { CreateDtoManager dtoManager = new CreateDtoManager(); var dbTables = dtoManager.GetDbTable(); var dtoPath = _configuration.GetSection("DbConfigInfo")["CodeResourcePath"]; var templateNames = _configuration.GetSection("DbConfigInfo")["BaseDtoTemplateName"]; foreach (var item in dbTables) { foreach (var templateName in templateNames?.Split(',').ToList()) { var result = Engine.Razor.Run(templateName.ToLower(), null, item); var filePath = $"{dtoPath}/BaseDtos/{item.SchemaName.GetPath('_')}/{item.TableName}s"; var prefix = ""; if (templateName.Contains("add", StringComparison.OrdinalIgnoreCase)) prefix = "Add"; if (templateName.Contains("all", StringComparison.OrdinalIgnoreCase)) prefix = "All"; if (templateName.Contains("edit", StringComparison.OrdinalIgnoreCase)) prefix = "Edit"; string fileName = filePath + "\\" + $"{prefix}Base{item.TableName}Dto.cs"; //儲存檔案 FileHelper.Save(fileName, result); } } return ResultTo.Success("生成成功"); }
讓我們看看效果
然後儲存在本地就大功告成了,我集中放到了一個資料夾,這樣方便直接拷貝替換(整合到專案可以直接替換,但是有覆蓋風險,沒敢),後面看看最後的成果。