ABPHelper.CLI及其依赖项简单介绍
目录
ABPHelper.CLI
- AbpHelper is a tool that helps you with developing Abp vNext applications.
- https://github.com/EasyAbp/AbpHelper.CLI
命令行CLI实现ABP VNEXT中CRUD代码的生成,用户只需要创建一个实体类,即可生成该表的CRUD,并添加到项目中。
使用前请确保备份您的源文件!
入门
安装 AbpHelper CLI 工具
dotnet tool install EasyAbp.AbpHelper -g
如果您更喜欢GUI,那么还有一个UI工具: AbpHelper.GUI
如果以前安装过,请使用以下命令更新它:
dotnet tool update EasyAbp.AbpHelper -g使用 ABP CLI 创建一个ABP 应用
abp new MyToDo创建实体
public class Todo : FullAuditedEntity<Guid>
{
public string Content { get; set; }
public bool Done { get; set; }
}
- 运行 AbpHelper
abphelper generate crud Todo -d C:\MyTodo
generate crud是生成CRUD文件的子命令Todo指定了我们先前创建的实体名-d指定了由ABP CLI创建的ABP项目的根目录
AbpHelper 将生成所有的CRUD , 甚至包括添加迁移和数据库更新!
- 运行这个
DbMigrator项目去迁移数据库 - 启动你的应用
- 用默认的管理员帐户登录,看到神奇的事发生了!

如果看不到 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
如果我们想实现代码生成器,我们需要解决什么问题呢。
- 提供.NET接口的模板引擎,比如Razor,Sciban等
- 模板一般放在文件中,我们需要知道如何读取这些资源文件,文本文件。
- 如果我们使用code first,通常需要创建一个实体类,当创建好一个类后,怎么解析出这个类名,属性,命名空间呢,而不是另外去输入这些参数。(只需要代码的路径)
- 实体名Name,比如BaseItem
- 命名空间NameSpace LinCms.Base.BaseItems或Volo.Abp.BaseItems
- 主键类型PrimaryKey,比如是Guid,还是int,还是long
- 明确有哪些变量,如何控制输入。
- 模板的位置TemplatePath : ./Templates
- 根据模板生成的代码的输出目录OutputDirectory : 相对路径 或 绝对路径 ./output 或 D:/code/github/code-scaffolding
Scriban
Scriban是一种快速、强大、安全和轻量级的文本模板语言和.NET引擎,具有解析liquid模板的兼容模式
- 【翻译】Scriban是一种快速、强大、安全和轻量级的文本模板语言和.NET引擎,具有解析liquid模板的兼容模式
- 【翻译】 Scriban language( 待完成)
- 【翻译】Scriban runtime( 待完成)
创建一个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
- https://github.com/EasyAbp/AbpHelper.GUI
- AbpHelper is a tool that helps you with developing Abp vNext applications. It can be used to call ABP CLI, generate code, manage modules, etc.
ABP VNEXT的代码生成的可视化界面,使用方式看README就好,不多介绍。
帮助ABP VNext的开发者快速构建单表的CRUD的。
如果你自己研究一下这些类库的使用方法,特别是Sciban。我们也能实现代码生成器,帮助改善公司及自己已有项目的开发流程。
ABPHelper.CLI及其依赖项简单介绍的更多相关文章
- C#中缓存的简单方法及使用Sql设置缓存依赖项
概述 使用Cache高速缓存可以提高数据的读取速度,减少服务器与客户端之间的数据交互.因为Cache一经创建就会占用服务器上的资源,所以Cache并不是越多越好,一般用于数据较固定,使用较频繁的地方. ...
- 简单介绍托管执行和 CLI
目录 CIL 和 ILDASM 查看 myApp.dll 的 CIL 输出 使用 ILSpy 查看 myApp.dll 反编译后的代码 处理器不能直接解释程序集.程序集用的是另一种语言,即公共中间语言 ...
- 简单介绍一下R中的几种统计分布及常用模型
统计学上分布有很多,在R中基本都有描述.因能力有限,我们就挑选几个常用的.比较重要的简单介绍一下每种分布的定义,公式,以及在R中的展示. 统计分布每一种分布有四个函数:d――density(密度函数) ...
- Maven仓库—Nexus环境搭建及简单介绍
1. 环境搭建 1.1 下载 http://www.sonatype.org/nexus/ NEXUS OSS [OSS = Open Source Software,开源软件--免费] NE ...
- SQLite数据库和JPA简单介绍
SQLite数据库和JPA简单介绍 一.SQLite简单使用 SQLite是遵循ACID的关系数据库管理系统,它的处理速度很快,它的设计目标是嵌入式的,只需要几百K的内存就可以了. 1.下载SQLit ...
- 决策树简单介绍(二) Accord.Net中决策树的实现和使用
决策树介绍 决策树是一类机器学习算法,可以实现对数据集的分类.预测等.具体请阅读我另一篇博客(http://www.cnblogs.com/twocold/p/5424517.html). Accor ...
- 导入时如何定制spring-boot依赖项的版本
spring-boot通过maven的依赖管理为我们写好了很多依赖项及其版本,我们可拿来使用.spring-boot文档介绍了两种使用方法,一是继承,二是导入. 通过<parent>继承: ...
- HTML简单介绍及举例
超文本标记语言(Hyper Text Markup Language,简称HTML)是为"网页创建和其他可在网页浏览器中看到的信息"设计的一种标记语言.HTML被用来结构化信息,也 ...
- C#进阶系列——使用Advanced Installer制作IIS安装包(二:配置安装包依赖项和自定义dll)
前言:上篇C#进阶系列——使用Advanced Installer制作IIS安装包(一:配置IIS和Web.config)介绍了下使用Advanced Installer配置IIS和Web.confi ...
随机推荐
- 「MoreThanJava」Day 3:构建程序逻辑的方法
「MoreThanJava」 宣扬的是 「学习,不止 CODE」,本系列 Java 基础教程是自己在结合各方面的知识之后,对 Java 基础的一个总回顾,旨在 「帮助新朋友快速高质量的学习」. 当然 ...
- videojs兼容ie8
从网上找到很多这个videojs兼容ie8的解决方案,一个一个的试,最后发现没有一个是靠谱的.我好无奈啊…… 先看图(ie上访问必须是线上地址) 看代码: <!DOCTYPE html> ...
- HDU 3911 Black and White (线段树,区间翻转)
[题目地址] vjudge HDU [题目大意] 海滩上有一堆石头. 石头的颜色是白色或黑色. 小肥羊拥有魔术刷,她可以改变连续石的颜色,从黑变白,从白变黑. 小肥羊非常喜欢黑色,因此她想知道范围 ...
- 「区间DP」「洛谷P1043」数字游戏
「洛谷P1043」数字游戏 日后再写 代码 /*#!/bin/sh dir=$GEDIT_CURRENT_DOCUMENT_DIR name=$GEDIT_CURRENT_DOCUMENT_NAME ...
- JS数组与对象赋值问题
在W3C的在线编程中经过测试发现以下问题: 当一个数组内部元素为对象时,给数组赋值应该先给对象赋值,然后把该对象push到数组中. 如下所示: 在控制台打印之后的数据格式为下图所示: 如果在给数组赋值 ...
- 性能测试之jmeter逻辑控制种类详解一
逻辑控制器介绍 Jmeter逻辑控制可以对元件的执行逻辑进行控制,除Once only Controller仅一次控制器以外,其他控制器都可以可以嵌套其他种类的控制器,下面是jmeter5.3支持的控 ...
- Python——读取大文件(GB)
最近处理文本文档时(文件约2GB大小),出现memoryError错误和文件读取太慢的问题,后来找到了两种比较快Large File Reading 的方法,本文将介绍这两种读取方法. Prelimi ...
- 隐写术工具之binwalk
0x00Binwalk介绍 Binwalk是用于搜索给定二进制镜像文件以获取嵌入的文件和代码的工具. 具体来说,它被设计用于识别嵌入固件镜像内的文件和代码. Binwalk使用libmagic库,因此 ...
- mysql numeric
tinyint 1个字节 smallint 2个字节 mediumint 3个字节 int 4个字节 bigint 8个字节
- Django之模型层第一篇:单表操作
Django之模型层第一篇:单表操作 一 ORM简介 我们在使用Django框架开发web应用的过程中,不可避免地会涉及到数据的管理操作(如增.删.改.查),而一旦谈到数据的管理操作,就需要用到数 ...