RazorEngine.NetCore 相见恨晚,它让我彻底放弃了T4模板
- 2022 年 1 月 6 日
- 筆記
在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("生成成功"); }
让我们看看效果
然后保存在本地就大功告成了,我集中放到了一个文件夹,这样方便直接拷贝替换(集成到项目可以直接替换,但是有覆盖风险,没敢),后面看看最后的成果。