目录

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. 用默认的管理员帐户登录,看到神奇的事发生了!

如果看不到 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("\r\n", "").Replace(" ", ""), result.Replace("\r\n", "").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。我们也能实现代码生成器,帮助改善公司及自己已有项目的开发流程。

ABPHelper.CLI及其依赖项简单介绍的更多相关文章

  1. C#中缓存的简单方法及使用Sql设置缓存依赖项

    概述 使用Cache高速缓存可以提高数据的读取速度,减少服务器与客户端之间的数据交互.因为Cache一经创建就会占用服务器上的资源,所以Cache并不是越多越好,一般用于数据较固定,使用较频繁的地方. ...

  2. 简单介绍托管执行和 CLI

    目录 CIL 和 ILDASM 查看 myApp.dll 的 CIL 输出 使用 ILSpy 查看 myApp.dll 反编译后的代码 处理器不能直接解释程序集.程序集用的是另一种语言,即公共中间语言 ...

  3. 简单介绍一下R中的几种统计分布及常用模型

    统计学上分布有很多,在R中基本都有描述.因能力有限,我们就挑选几个常用的.比较重要的简单介绍一下每种分布的定义,公式,以及在R中的展示. 统计分布每一种分布有四个函数:d――density(密度函数) ...

  4. Maven仓库—Nexus环境搭建及简单介绍

    1.    环境搭建 1.1  下载 http://www.sonatype.org/nexus/ NEXUS OSS [OSS = Open Source Software,开源软件--免费] NE ...

  5. SQLite数据库和JPA简单介绍

    SQLite数据库和JPA简单介绍 一.SQLite简单使用 SQLite是遵循ACID的关系数据库管理系统,它的处理速度很快,它的设计目标是嵌入式的,只需要几百K的内存就可以了. 1.下载SQLit ...

  6. 决策树简单介绍(二) Accord.Net中决策树的实现和使用

    决策树介绍 决策树是一类机器学习算法,可以实现对数据集的分类.预测等.具体请阅读我另一篇博客(http://www.cnblogs.com/twocold/p/5424517.html). Accor ...

  7. 导入时如何定制spring-boot依赖项的版本

    spring-boot通过maven的依赖管理为我们写好了很多依赖项及其版本,我们可拿来使用.spring-boot文档介绍了两种使用方法,一是继承,二是导入. 通过<parent>继承: ...

  8. HTML简单介绍及举例

    超文本标记语言(Hyper Text Markup Language,简称HTML)是为"网页创建和其他可在网页浏览器中看到的信息"设计的一种标记语言.HTML被用来结构化信息,也 ...

  9. C#进阶系列——使用Advanced Installer制作IIS安装包(二:配置安装包依赖项和自定义dll)

    前言:上篇C#进阶系列——使用Advanced Installer制作IIS安装包(一:配置IIS和Web.config)介绍了下使用Advanced Installer配置IIS和Web.confi ...

随机推荐

  1. 总结几个移动端H5软键盘的大坑

    1.部分机型软键盘弹起挡住原来的视图 解决方法:可以通过监听移动端软键盘弹起 Element.scrollIntoView() 方法让当前的元素滚动到浏览器窗口的可视区域内.参数如下. true,表示 ...

  2. HDU 2236 无题II 题解

    题目 这是一个简单的游戏,在一个n*n的矩阵中,找n个数使得这n个数都在不同的行和列里并且要求这n个数中的最大值和最小值的差值最小. 输入格式 输入一个整数\(T\)表示\(T\)组数据. 对于每组数 ...

  3. Java入门系列之访问修饰符作用范围

    前言 之前以为Java和C#中访问修饰符差不多一样,后面才了解到还是有些差异,本节只讲解学习Java中访问修饰符一些需要注意的地方或者从概念上不太好理解我们会通过实际例子来说明,若有错误之处,还请批评 ...

  4. @Autowired 引发的一系列思考

    关于Java注解 注解定义 标记注解 - 没有元素 @interface Marker { } 单元素注解 - 只有一个元素 @interface Single { String value() de ...

  5. 数据库/MySQL的安装

    来源:https://www.cnblogs.com/liubing8/p/11431382.html mysql的安装.启动和基础配置 —— windows版本 1.下载 第一步:打开网址,http ...

  6. python 面试题一:Python语言特性

    1 Python的函数参数传递 两个例子 a = 1 def fun(a): a = 2 fun(a) print a # a = [] def fun(a): a.append(1) fun(a) ...

  7. python数据处理(七)之数据探索和分析

    1.探索数据 1.1 安装agate库 1.2 导入数据 1.3 探索表函数 a.排序 b.最值,均值 c.清除缺失值 d.过滤 e.百分比 1.4 连结多个数据集 a.捕捉异常 b.去重 c.缺失数 ...

  8. java 面向对象(三):类结构 属性

    类的设计中,两个重要结构之一:属性 对比:属性 vs 局部变量 1.相同点: * 1.1 定义变量的格式:数据类型 变量名 = 变量值 * 1.2 先声明,后使用 * 1.3 变量都其对应的作用域 2 ...

  9. 数据分析01 /numpy模块

    数据分析01 /数据分析之numpy模块 目录 数据分析01 /数据分析之numpy模块 1. numpy简介 2. numpy的创建 3. numpy的方法 4. numpy的常用属性 5. num ...

  10. SpringBoot2.x入门:使用CommandLineRunner钩子接口

    前提 这篇文章是<SpringBoot2.x入门>专辑的第6篇文章,使用的SpringBoot版本为2.3.1.RELEASE,JDK版本为1.8. 这篇文章主要简单聊聊钩子接口Comma ...