zoukankan      html  css  js  c++  java
  • ABPHelper.CLI及其依赖项简单介绍

    目录

    ABPHelper.CLI

    命令行CLI实现ABP VNEXT中CRUD代码的生成,用户只需要创建一个实体类,即可生成该表的CRUD,并添加到项目中。

    使用前请确保备份您的源文件!

    入门

    1. 安装 AbpHelper CLI 工具

      dotnet tool install EasyAbp.AbpHelper -g

    如果您更喜欢GUI,那么还有一个UI工具: AbpHelper.GUI

    1. 如果以前安装过,请使用以下命令更新它:

      dotnet tool update EasyAbp.AbpHelper -g

    2. 使用 ABP CLI 创建一个ABP 应用

      abp new MyToDo

    3. 创建实体

    public class Todo : FullAuditedEntity<Guid>
    {
        public string Content { get; set; }
        public bool Done { get; set; }
    }
    
    
    1. 运行 AbpHelper

    abphelper generate crud Todo -d C:MyTodo

    • generate crud 是生成CRUD文件的子命令
    • Todo 指定了我们先前创建的实体名
    • -d 指定了由ABP CLI创建的ABP项目的根目录

    AbpHelper 将生成所有的CRUD , 甚至包括添加迁移和数据库更新!

    1. 运行这个 DbMigrator 项目去迁移数据库
    2. 启动你的应用
    3. 用默认的管理员帐户登录,看到神奇的事发生了!

    运行demo

    如果看不到 TODO 菜单,请检查您的权限并确保授予了TODO相关的权限

    使用指南

    • 运行abphelper -h 查看帮助
    • 类似地,您可以使用 -h--help 选项查看以下每个命令的详细用法

    命令行

    • generate

      为ABP项目生成文件. 使用 'abphelper generate --help' 获取详情

      • crud

        根据指定实体生成一组与CRUD相关的文件

        abphelper generate crud Todo
        

      • service

      根据指定的名称生成服务接口和类文件

      abphelper generate service Project -f Projects
      

      • methods

        Generate service method(s) according to the specified name(s)

        根据指定名称,给service 增加方法

        abphelper generate methods Login Logout -s Project
        

      • localization

        Generate localization item(s) according to the specified name(s)

        根据指定名称生成localization 本地化项

        abphelper generate localization MyItem1 MyItem2 MyItem3
        

      • controller

        abphelper generate controller Todo
        

        Generate controller class and methods according to the specified service

    技术点如下

    • Scriban
    • Microsoft.Extensions.FileProviders.Embedded
    • Microsoft.CodeAnalysis.CSharp
    • System.CommandLine
    • Elsa
    • Humanizer.Core

    如果我们想实现代码生成器,我们需要解决什么问题呢。

    1. 提供.NET接口的模板引擎,比如Razor,Sciban等
    2. 模板一般放在文件中,我们需要知道如何读取这些资源文件,文本文件。
    3. 如果我们使用code first,通常需要创建一个实体类,当创建好一个类后,怎么解析出这个类名,属性,命名空间呢,而不是另外去输入这些参数。(只需要代码的路径)
      1. 实体名Name,比如BaseItem
      2. 命名空间NameSpace LinCms.Base.BaseItems或Volo.Abp.BaseItems
      3. 主键类型PrimaryKey,比如是Guid,还是int,还是long
    4. 明确有哪些变量,如何控制输入。
      1. 模板的位置TemplatePath : ./Templates
      2. 根据模板生成的代码的输出目录OutputDirectory : 相对路径 或 绝对路径 ./outputD:/code/github/code-scaffolding

    Scriban

    Scriban是一种快速、强大、安全和轻量级的文本模板语言和.NET引擎,具有解析liquid模板的兼容模式

    创建一个xunit测试项目,引入包Sciban

    <PackageReference Include="Scriban" Version="3.0.0-alpha.3" />
    

    做一个小测试

    [Fact]
    public void Test1()
    {
        var template = Template.Parse("Hello {{name}}!");
        var result = template.Render(new { Name = "World" });
        Assert.Equal("Hello World!", result);
    }
    

    ctrl+r ctrl+t 运行测试,正常。

    写一个我们仓储接口。

         [Fact]
            public void Test9()
            {
                var template = Template.Parse(@"using LinCms.Core.Entities;
    namespace LinCms.Core.IRepositories
    {
        public interface I{{ entity_name }}Repository : IAuditBaseRepository<{{ entity_name }}>
        {
    
        }
    }");
                var result = template.Render(new { EntityName = "Doc" });
                Assert.Equal(@"using LinCms.Core.Entities;
    namespace LinCms.Core.IRepositories
        {
            public interface IDocRepository : IAuditBaseRepository<Doc>
            {
    
            }
        }".Replace("
    ", "").Replace(" ", ""), result.Replace("
    ", "").Replace(" ", ""));
            }
    
    

    最终生成的效果是

    using LinCms.Core.Entities;
    namespace LinCms.Core.IRepositories
    {
        public interface IDocRepository : IAuditBaseRepository<Doc>
        {
    
        }
    }
    

    通过Microsoft.Extensions.FileProviders.Embedded获取嵌入资源

    这是一个嵌入资源Provider,提供嵌入资源的获取,比如我们写的Sciban的模板文件。

    xunit测试项目引入包

    <PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.6" />
    

    创建一个测试类FileTest

    
    [Fact]
    public void FileProviderTest()
    {
        IFileProvider fileProvider = new ManifestEmbeddedFileProvider(Assembly.GetAssembly(typeof(FileTest)));
    }
    

    出现这个错
    System.InvalidOperationException:“Could not load the embedded file manifest 'Microsoft.Extensions.FileProviders.Embedded.Manifest.xml' for assembly 'OvOv.Test'.”

    打开OvOv.Test.csproject
    PropertyGroup增加如下一行
    true

    新建目录Templates,新建文本文件 IRepository.txt,右键属性,生成操作(嵌入的资源),可不选复制到输出目录

    using LinCms.Core.Entities;
    namespace LinCms.Core.IRepositories
    {
        public interface I{{ entity_ame }}Repository : IAuditBaseRepository<{{ entity_ame }}>
        {
    
        }
    }
    

    可修改csproject文件设置Templates目录下都是嵌入式资源

    <ItemGroup>
    	<EmbeddedResource Include="Templates****" />
    </ItemGroup>
    

    在测试方法中,通过GetFileInfo,得到IFileInfo.通过stream进行读取文本,并输出。

    private readonly ITestOutputHelper output;
    public FileTest(ITestOutputHelper output)
    {
        this.output = output;
    }
    
    [Fact]
    public void GetTextTest()
    {
        IFileProvider fileProvider = new ManifestEmbeddedFileProvider(Assembly.GetAssembly(typeof(FileTest)));
        IFileInfo fileInfo = fileProvider.GetFileInfo("./Templates/IRepository.txt");
        string text;
        using (var stream = fileInfo.CreateReadStream())
        {
            using (var streamReader = new StreamReader(stream, Encoding.UTF8, true))
            {
                text = streamReader.ReadToEnd();
            }
        }
        output.WriteLine(text);
    }
    

    通过静态方法获取文件内容

    不用嵌入式资源,需要右键文件 属性(复制到输出目录:如果较新则复制),可不选生成操作

    [Fact]
    public void ReadAllText()
    {
        string text = File.ReadAllText("./Templates/IRepository.txt");
        output.WriteLine(text);
    }
    

    使用Microsoft.Extensions.FileProviders.Physical获取文件内容

    引用包

    <PackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="3.1.6" />
    
    [Theory]
    [InlineData("./Templates/IRepository.txt")]
    public void PhysicalFileProviderReadText(string path)
    {
        var current = Environment.CurrentDirectory;
        var fileProvider = new PhysicalFileProvider(current);
        IFileInfo fileInfo = fileProvider.GetFileInfo(path);
        string text;
        using (var stream = fileInfo.CreateReadStream())
        {
            using (var streamReader = new StreamReader(stream, Encoding.UTF8, true))
            {
                text = streamReader.ReadToEnd();
            }
        }
        output.WriteLine(text);
    }
    

    var current = Environment.CurrentDirectory; 这行代码,能得到当前的目录,因为设置为输出 ,所以当前目录下有templates文件夹,并有IRepository.txt

    "D:\code\gitee\Code\OvOv.Test\bin\Debug\netcoreapp3.1"
    

    Microsoft.CodeAnalysis.CSharp

    代码生成还需要什么呢,创建一个实体类,根据此实体类生成表的CRUD代码。

    修改OvOv.Test,引入包

     <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.6.0" />
    

    这个包是https://github.com/dotnet/roslyn的一部分.Roslyn 是.NET编译器为C#和Visual Basic 提供了丰富的代码分析API。

    再搞个CodeAnalysisTest测试类。这里我们有一个字符串,他是一个实体类,这个类有一些特点。比如

    • 命名空间namespace
    • 类名 class
    • 继承的父类泛型类型 guid
    • 有二个属性,author,title

    如下代码,通过语法树,解析出这个字符串中的命名空间,输出 LinCms.Books。

    说明: FullAduitEntity是一个包含CRUD的审计实体类,通常包含七个字段(创建人,创建时间,修改人,修改时间,是否删除,删除人,删除时间),另外还有一个主键。默认是long,具体可查看此文件https://github.com/luoyunchong/lin-cms-dotnetcore/blob/master/src/LinCms.Core/Entities/FullAduitEntity.cs

    你也可以使用诸如Entity,即一个泛型的实体类即可。

    public class Entity<T>
    {
        public T Id { get; set; }
    }
    
    [Fact]
    public void GetNamespace()
    {
        string text = @"
    namespace LinCms.Books
    {
        public class Book : FullAduitEntity<Guid>
        {
            public string Author { get; set; }
    
            public string Title { get; set; } 
        }
    }";
    
        SyntaxTree tree = CSharpSyntaxTree.ParseText(text);
        CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
        var @namespace = root.DescendantNodes().OfType<NamespaceDeclarationSyntax>().Single().Name.ToString();
        output.WriteLine(@namespace);    
    }
    

    获取类名.className为Book

    ClassDeclarationSyntax classDeclarationSyntax = root.DescendantNodes().OfType<ClassDeclarationSyntax>().Single();
    string className = classDeclarationSyntax.Identifier.ToString();
    

    获取父类baseType为FullAduitEntity
    主键类型primaryKey值为Guid

        BaseListSyntax baseList = classDeclarationSyntax.BaseList!;
    var genericNameSyntax = baseList.DescendantNodes().OfType<SimpleBaseTypeSyntax>()
        .First(node => !node.ToFullString().StartsWith("I")) // Not interface
        .DescendantNodes().OfType<GenericNameSyntax>()
        .FirstOrDefault();
    
    string baseType;
    string? primaryKey;
    if (genericNameSyntax == null)
    {
        // No generic parameter -> Entity with Composite Keys
        baseType = baseList.DescendantNodes().OfType<SimpleBaseTypeSyntax>().Single().Type.ToString();
        primaryKey = "long";
    }
    else
    {
        // Normal entity
        baseType = genericNameSyntax.Identifier.ToString();
        primaryKey = genericNameSyntax.DescendantNodes().OfType<TypeArgumentListSyntax>().Single().Arguments[0].ToString();
    }
    

    获取该类的属性集合。

    var properties = root.DescendantNodes().OfType<PropertyDeclarationSyntax>()
                      .Select(prop => new PropertyInfo(prop.Type.ToString(), prop.Identifier.ToString()))
                      .ToList();
    

    其中PropertyInfo是用来存储属性集合的实体类

    public class PropertyInfo
    {
        public string Type { get; }
    
        public string Name { get; }
    
        public PropertyInfo(string type, string name)
        {
            Type = type;
            Name = name;
        }
    }
    

    我们通过debugger查看局部变量。

    Humanizer.Core

    Humanizer可以用来处理strings, enums, dates, times, timespans, numbers and quantities所有的需求。

    当我们写代码时,总避免不了写复数形式的代码,一些特殊的后缀不是直接加s就行的。
    所以可以用Humanizer来处理这些特殊的变量

    • 转下划线 Underscore
    • 复数形式(比如取集合数据时,变量名) Pluralize
    • 转小驼峰写法(比如变量) Camelize
    • 更多直接看README

    通过扩展方法生成这些字符串。

    public class EntityInfo
    {
        public EntityInfo(string name)
        {
            Name = name;
        }
    
        public string Name { get; }
    
        /// <summary>
        /// 复数
        /// </summary>
        public string NamePluralized => Name.Pluralize();
        /// <summary>
        /// 首字母小写
        /// </summary>
        public string NameCamelize => Name.Camelize();
    
        /// <summary>
        /// 小写+复数
        /// </summary>
        public string NameCamelizePluralized => Name.Camelize().Pluralize();
    
    }
    

    System.CommandLine

    System.CommandLine是一组用于构建命令行应用程序的库,包括解析,调用和渲染。

    他能简化命令行参数的处理,帮助我们构建自己的CLI。

    对于代码生成器,不要是必须的。

    Elsa

    Elsa Core是一个工作流库,可在任何 .NET Core应用程序中执行工作流。工作流可以不仅使用代码来定义,也可作为JSON,YAML或XML。

    代码生成器,流程多,可使用此程序工作流来处理。不是必须的。

    AbpHelper.GUI

    ABP VNEXT的代码生成的可视化界面,使用方式看README就好,不多介绍。

    帮助ABP VNext的开发者快速构建单表的CRUD的。

    如果你自己研究一下这些类库的使用方法,特别是Sciban。我们也能实现代码生成器,帮助改善公司及自己已有项目的开发流程。

  • 相关阅读:
    浅谈python web三大框架
    Mysql异常
    格式化时间转换
    MySql存储日期为long型,判断时间大小
    Linux下部署项目
    excel
    Tomcate的启动问题
    计算时间差
    地图系的转换
    关于获取本机真实IP
  • 原文地址:https://www.cnblogs.com/igeekfan/p/abphelper-cli-start.html
Copyright © 2011-2022 走看看