手把手教你实现自己的abp代码生成器
代码生成器的原理无非就是得到字段相关信息(字段名,字段类型,字段注释等),然后根据模板,其实就是字符串的拼接与替换生成相应代码。
所以第一步我们需要解决如何得到字段的相关信息,有两种方式
- 通过反射获得程序集类的字段相关信息
- 读取数据库得到表的字段的相关信息
新建一个.NET Core控制台项目 取名AbpCodeGenerator
新建类DocsByReflection
/// <summary>
/// Utility class to provide documentation for various types where available with the assembly
/// </summary>
public class DocsByReflection
{
/// <summary>
/// Provides the documentation comments for a specific method
/// </summary>
/// <param name="methodInfo">The MethodInfo (reflection data ) of the member to find documentation for</param>
/// <returns>The XML fragment describing the method</returns>
public static XmlElement XMLFromMember(MethodInfo methodInfo)
{
// Calculate the parameter string as this is in the member name in the XML
string parametersString = "";
foreach (ParameterInfo parameterInfo in methodInfo.GetParameters())
{
if (parametersString.Length > 0)
{
parametersString += ",";
} parametersString += parameterInfo.ParameterType.FullName;
} //AL: 15.04.2008 ==> BUG-FIX remove ?)?if parametersString is empty
if (parametersString.Length > 0)
return XMLFromName(methodInfo.DeclaringType, 'M', methodInfo.Name + "(" + parametersString + ")");
else
return XMLFromName(methodInfo.DeclaringType, 'M', methodInfo.Name);
} /// <summary>
/// Provides the documentation comments for a specific member
/// </summary>
/// <param name="memberInfo">The MemberInfo (reflection data) or the member to find documentation for</param>
/// <returns>The XML fragment describing the member</returns>
public static XmlElement XMLFromMember(MemberInfo memberInfo)
{
// First character [0] of member type is prefix character in the name in the XML
return XMLFromName(memberInfo.DeclaringType, memberInfo.MemberType.ToString()[0], memberInfo.Name);
} /// <summary>
/// Provides the documentation comments for a specific type
/// </summary>
/// <param name="type">Type to find the documentation for</param>
/// <returns>The XML fragment that describes the type</returns>
public static XmlElement XMLFromType(Type type)
{
// Prefix in type names is T
return XMLFromName(type, 'T', "");
} /// <summary>
/// Obtains the XML Element that describes a reflection element by searching the
/// members for a member that has a name that describes the element.
/// </summary>
/// <param name="type">The type or parent type, used to fetch the assembly</param>
/// <param name="prefix">The prefix as seen in the name attribute in the documentation XML</param>
/// <param name="name">Where relevant, the full name qualifier for the element</param>
/// <returns>The member that has a name that describes the specified reflection element</returns>
private static XmlElement XMLFromName(Type type, char prefix, string name)
{
string fullName; if (String.IsNullOrEmpty(name))
{
fullName = prefix + ":" + type.FullName;
}
else
{
fullName = prefix + ":" + type.FullName + "." + name;
} XmlDocument xmlDocument = XMLFromAssembly(type.Assembly); XmlElement matchedElement = null; foreach (XmlElement xmlElement in xmlDocument["doc"]["members"])
{
if (xmlElement.Attributes["name"].Value.Equals(fullName))
{
if (matchedElement != null)
{
throw new DocsByReflectionException("Multiple matches to query", null);
} matchedElement = xmlElement;
break;
}
} if (matchedElement == null)
{
throw new DocsByReflectionException("Could not find documentation for specified element", null);
} return matchedElement;
} /// <summary>
/// A cache used to remember Xml documentation for assemblies
/// </summary>
static Dictionary<Assembly, XmlDocument> cache = new Dictionary<Assembly, XmlDocument>(); /// <summary>
/// A cache used to store failure exceptions for assembly lookups
/// </summary>
static Dictionary<Assembly, Exception> failCache = new Dictionary<Assembly, Exception>(); /// <summary>
/// Obtains the documentation file for the specified assembly
/// </summary>
/// <param name="assembly">The assembly to find the XML document for</param>
/// <returns>The XML document</returns>
/// <remarks>This version uses a cache to preserve the assemblies, so that
/// the XML file is not loaded and parsed on every single lookup</remarks>
public static XmlDocument XMLFromAssembly(Assembly assembly)
{
if (failCache.ContainsKey(assembly))
{
throw failCache[assembly];
} try
{ if (!cache.ContainsKey(assembly))
{
// load the docuemnt into the cache
cache[assembly] = XMLFromAssemblyNonCached(assembly);
} return cache[assembly];
}
catch (Exception exception)
{
failCache[assembly] = exception;
throw exception;
}
} /// <summary>
/// Loads and parses the documentation file for the specified assembly
/// </summary>
/// <param name="assembly">The assembly to find the XML document for</param>
/// <returns>The XML document</returns>
private static XmlDocument XMLFromAssemblyNonCached(Assembly assembly)
{
string assemblyFilename = assembly.CodeBase; const string prefix = "file:///"; if (assemblyFilename.StartsWith(prefix))
{
using (StreamReader streamReader = new StreamReader(Path.ChangeExtension(assemblyFilename.Substring(prefix.Length), ".xml")))
{
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(streamReader); return xmlDocument;
}
}
else
{
throw new DocsByReflectionException("Could not ascertain assembly filename", null);
}
}
} /// <summary>
/// An exception thrown by the DocsByReflection library
/// </summary>
[Serializable]
class DocsByReflectionException : Exception
{
/// <summary>
/// Initializes a new exception instance with the specified
/// error message and a reference to the inner exception that is the cause of
/// this exception.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or null if none.</param>
public DocsByReflectionException(string message, Exception innerException)
: base(message, innerException)
{ }
}
新建类MetaTableInfo
public class MetaTableInfo
{
/// <summary>
/// 类的注释
/// </summary>
public string ClassAnnotation { get; set; } /// <summary>
/// 属性名称
/// </summary>
public string Name { get; set; } /// <summary>
/// 属性类型
/// </summary>
public string PropertyType { get; set; } /// <summary>
/// 属性注释
/// </summary>
public string Annotation { get; set; } /// <summary>
/// 根据类名 反射得到类的信息
/// </summary>
/// <param name="className">类名</param>
/// <returns></returns>
public static List<MetaTableInfo> GetMetaTableInfoListForAssembly(string className)
{ var list = new List<MetaTableInfo>();
//读取的程序集路径 需换成自己项目的下的程序集路径
string sourceAssemblyPath = "D:\\Project\\ZhouDaFu.XinYunFen.Core\\bin\\Debug\\netcoreapp2.0\\ZhouDaFu.XinYunFen.Core.dll";
Type[] types = Assembly.LoadFrom(sourceAssemblyPath).GetTypes();
foreach (var type in types)
{
if (type.Name.Equals(className))
{
var classAnnotation = string.Empty;
try
{
//获取类的注释
XmlElement xmlFromType = DocsByReflection.XMLFromType(type.GetTypeInfo());
classAnnotation = xmlFromType["summary"].InnerText.Trim();
}
catch
{ } foreach (PropertyInfo properties in type.GetProperties())
{
var metaTableInfo = new MetaTableInfo();
try
{
XmlElement documentation = DocsByReflection.XMLFromMember(type.GetProperty(properties.Name));
metaTableInfo.Annotation = documentation["summary"].InnerText.Trim(); metaTableInfo.ClassAnnotation = classAnnotation;
}
catch
{
metaTableInfo.Annotation = "";
}
metaTableInfo.Name = properties.Name;
if (properties.PropertyType == typeof(int))
{
metaTableInfo.PropertyType = "int";
}
else if (properties.PropertyType == typeof(int?))
{
metaTableInfo.PropertyType = "int?";
}
else if (properties.PropertyType == typeof(long))
{
metaTableInfo.PropertyType = "long";
}
else if (properties.PropertyType == typeof(long?))
{
metaTableInfo.PropertyType = "long?";
}
else if (properties.PropertyType == typeof(DateTime?))
{
metaTableInfo.PropertyType = "DateTime?";
}
else if (properties.PropertyType == typeof(decimal))
{
metaTableInfo.PropertyType = "decimal";
}
else if (properties.PropertyType == typeof(decimal?))
{
metaTableInfo.PropertyType = "decimal?";
}
else if (properties.PropertyType == typeof(string))
{
metaTableInfo.PropertyType = "string";
}
else if (properties.PropertyType == typeof(bool))
{
metaTableInfo.PropertyType = "bool";
}
else
{
metaTableInfo.PropertyType = properties.PropertyType.ToString().Split('.').Last().Replace("]", "");
}
list.Add(metaTableInfo);
}
}
} return list;
} }
在Program类中添加如下代码
//反射程序集的方式生成相应代码
string className = "User";//跟类名保持一致
var metaTableInfoList = MetaTableInfo.GetMetaTableInfoListForAssembly(className);
启动控制台,获得User相关信息,报如下错误
这是缺少ABP相关程序集引用,使用NuGet安装如下程序集
启动控制台得到如下结果,剩下的就是字符串拼接了
新建一个txt文件模板(直接在代码里面拼接字符串也行,只不过那样修改起来麻烦),取名IndexJsTemplate.txt,添加如下代码
(function () {
$(function () { var _${{entity_Name_Plural_Here}}Table = $('#MainTable');
var _{{entity_Name_Plural_Here}}Service = abp.services.app.{{entity_Name_Plural_Here}}; var _permissions = {
create: abp.auth.hasPermission('{{Permission_Value_Here}}.Create'),
edit: abp.auth.hasPermission('{{Permission_Value_Here}}.Edit'),
'delete': abp.auth.hasPermission('{{Permission_Value_Here}}.Delete')
}; var _createOrEditModal = new app.ModalManager({
viewUrl: abp.appPath + '{{App_Area_Name_Here}}/{{Entity_Name_Plural_Here}}/CreateOrEditModal',
scriptUrl: abp.appPath + 'view-resources/Areas/{{App_Area_Name_Here}}/Views/{{Entity_Name_Plural_Here}}/_CreateOrEditModal.js',
modalClass: 'CreateOrEdit{{Entity_Name_Here}}Modal'
}); var dataTable = _${{entity_Name_Plural_Here}}Table.DataTable({
paging: true,
serverSide: true,
processing: true,
listAction: {
ajaxFunction: _{{entity_Name_Plural_Here}}Service.getAll,
inputFilter: function () {
return {
filter: $('#{{Entity_Name_Plural_Here}}TableFilter').val()
};
}
},
columnDefs: [
{
width: 120,
targets: 0,
data: null,
orderable: false,
autoWidth: false,
defaultContent: '',
rowAction: {
cssClass: 'btn btn-brand dropdown-toggle',
text: '<i class="fa fa-cog"></i> ' + app.localize('Actions') + ' <span class="caret"></span>',
items: [
{
text: app.localize('Edit'),
visible: function () {
return _permissions.edit;
},
action: function (data) {
_createOrEditModal.open({ id: data.record.id });
}
}, {
text: app.localize('Delete'),
visible: function () {
return _permissions.delete;
},
action: function (data) {
delete{{Entity_Name_Here}}(data.record);
}
}]
}
}{{Property_Looped_Template_Here}}
]
}); function get{{Entity_Name_Plural_Here}}() {
dataTable.ajax.reload();
} function delete{{Entity_Name_Here}}({{entity_Name_Here}}) {
abp.message.confirm(
'',
function (isConfirmed) {
if (isConfirmed) {
_{{entity_Name_Plural_Here}}Service.delete({
id: {{entity_Name_Here}}.id
}).done(function () {
get{{Entity_Name_Plural_Here}}(true);
abp.notify.success(app.localize('SuccessfullyDeleted'));
});
}
}
);
} $('#Export{{Entity_Name_Here}}ToExcelButton').click(function () {
_{{entity_Name_Here}}Service
.get{{Entity_Name_Here}}ToExcel({})
.done(function (result) {
app.downloadTempFile(result);
});
}); $('#CreateNew{{Entity_Name_Here}}Button').click(function () {
_createOrEditModal.open();
}); abp.event.on('app.createOrEdit{{Entity_Name_Here}}ModalSaved', function () {
get{{Entity_Name_Plural_Here}}();
}); $('#Get{{Entity_Name_Plural_Here}}Button').click(function (e) {
e.preventDefault();
get{{Entity_Name_Plural_Here}}();
}); $(document).keypress(function(e) {
if(e.which === 13) {
get{{Entity_Name_Plural_Here}}();
}
}); });
})();
首先读取文件模板,然后去替换掉“{{}}” 这种类似占位符的变量,然后再输出一个新的文件,代码如下
static void Main(string[] args)
{
//反射程序集的方式生成相应代码
string className = "User";//跟类名保持一致
var metaTableInfoList = MetaTableInfo.GetMetaTableInfoListForAssembly(className);
SetIndexJsTemplate(className, metaTableInfoList);
} /// <summary>
/// 生成IndexJsTemplate
/// </summary>
/// <param name="className"></param>
public static void SetIndexJsTemplate(string className, List<MetaTableInfo> metaTableInfoList)
{
var rootPath = (Directory.GetCurrentDirectory().Split(new string[] { "bin" }, StringSplitOptions.RemoveEmptyEntries))[0];
string templateContentDirectory = rootPath + "IndexJsTemplate.txt";
var templateContent = Read(templateContentDirectory); StringBuilder sb = new StringBuilder();
var i = 1;
foreach (var item in metaTableInfoList)
{
sb.AppendLine(", {");
sb.AppendLine("targets: " + i + ",");
sb.AppendLine("data: \"" + GetFirstToLowerStr(item.Name) + "\"");
sb.AppendLine("}");
i++;
}
var property_Looped_Template_Here = sb.ToString();
templateContent = templateContent
.Replace("{{Entity_Name_Plural_Here}}", className)
.Replace("{{Entity_Name_Here}}", className)
.Replace("{{entity_Name_Here}}", GetFirstToLowerStr(className))
.Replace("{{entity_Name_Plural_Here}}", GetFirstToLowerStr(className))
.Replace("{{App_Area_Name_Here}}", "App_Area_Name")
.Replace("{{Property_Looped_Template_Here}}", property_Looped_Template_Here)
.Replace("{{Permission_Value_Here}}", "Pages.Administration." + className + "")
;
Write(rootPath, "Index.js", templateContent);
} #region 文件读取
public static string Read(string path)
{
using (StreamReader sr = new StreamReader(path, Encoding.Default))
{
StringBuilder sb = new StringBuilder(); String line;
while ((line = sr.ReadLine()) != null)
{
sb.AppendLine(line.ToString());
}
return sb.ToString();
}
} /// <summary>
///
/// </summary>
/// <param name="filePath">文件保存路径</param>
/// <param name="fileName">文件名</param>
/// <param name="templateContent">模板内容</param>
public static void Write(string filePath, string fileName, string templateContent)
{
if (!Directory.Exists(filePath))
{
Directory.CreateDirectory(filePath);
}
using (FileStream fs = new FileStream(filePath + fileName, FileMode.Create))
{
//获得字节数组
byte[] data = Encoding.Default.GetBytes(templateContent);
//开始写入
fs.Write(data, 0, data.Length);
} } /// <summary>
///
/// </summary>
/// <param name="filePath">文件保存路径</param>
/// <param name="templateContent">模板内容</param>
public static void Write(string filePath, string templateContent)
{
using (FileStream fs = new FileStream(filePath, FileMode.Create))
{
//获得字节数组
byte[] data = Encoding.Default.GetBytes(templateContent);
//开始写入
fs.Write(data, 0, data.Length);
} }
#endregion #region 首字母小写
/// <summary>
/// 首字母小写
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static string GetFirstToLowerStr(string str)
{
if (!string.IsNullOrEmpty(str))
{
if (str.Length > 1)
{
return char.ToLower(str[0]) + str.Substring(1);
}
return char.ToLower(str[0]).ToString();
}
return null;
}
#endregion
}
启动控制台,即在当前项目下生成相应代码,这里分享一个我之前写好的基于abp.zero core 5.3.0的
手把手教你实现自己的abp代码生成器的更多相关文章
- 手把手教你用动软.NET代码生成器实例教程
动软实战攻略 手把手教你用动软 文档编号:20110421 版权所有 © 2004-2011 动软 在线帮助:http://help.maticsoft.com 目录 一. 产品介绍 ...
- 手把手教你使用FineUI+动软代码生成器开发一个b/s结构的取送货管理信息系统(附源码)之开篇
一 本系列随笔概览及产生的背景 近阶段接到一些b/s类型的软件项目,但是团队成员之前大部分没有这方面的开发经验,于是自己选择了一套目前网上比较容易上手的开发框架(FineUI),计划录制一套视频讲座, ...
- 手把手教你使用FineUI开发一个b/s结构的取送货管理信息系统系列博文索引
近阶段接到一些b/s类型的软件项目,但是团队成员之前大部分没有这方面的开发经验,于是自己选择了一套目前网上比较容易上手的开发框架(FineUI),计划录制一套视频讲座,来讲解如何利用FineUI快速开 ...
- 手把手教你做个人 app
我们都知道,开发一个app很大程度依赖服务端:服务端提供接口数据,然后我们展示:另外,开发一个app,还需要美工协助切图.没了接口,没了美工,app似乎只能做成单机版或工具类app,真的是这样的吗?先 ...
- 手把手教从零开始在GitHub上使用Hexo搭建博客教程(四)-使用Travis自动部署Hexo(2)
前言 前面一篇文章介绍了Travis自动部署Hexo的常规使用教程,也是个人比较推荐的方法. 前文最后也提到了在Windows系统中可能会有一些小问题,为了在Windows系统中也可以实现使用Trav ...
- 手把手教从零开始在GitHub上使用Hexo搭建博客教程(三)-使用Travis自动部署Hexo(1)
前言 前面两篇文章介绍了在github上使用hexo搭建博客的基本环境和hexo相关参数设置等. 基于目前,博客基本上是可以完美运行了. 但是,有一点是不太好,就是源码同步问题,如果在不同的电脑上写文 ...
- 手把手教从零开始在GitHub上使用Hexo搭建博客教程(二)-Hexo参数设置
前言 前文手把手教从零开始在GitHub上使用Hexo搭建博客教程(一)-附GitHub注册及配置介绍了github注册.git相关设置以及hexo基本操作. 本文主要介绍一下hexo的常用参数设置. ...
- 手把手教从零开始在GitHub上使用Hexo搭建博客教程(一)-附GitHub注册及配置
前言 有朋友问了我关于博客系统搭建相关的问题,由于是做开发相关的工作,我给他推荐的是使用github的gh-pages服务搭建个人博客. 推荐理由: 免费:github提供gh-pages服务是免费的 ...
- UWP Jenkins + NuGet + MSBuild 手把手教你做自动UWP Build 和 App store包
背景 项目上需要做UWP的自动安装包,在以前的公司接触的是TFS来做自动build. 公司要求用Jenkins来做,别笑话我,之前还真不晓得这个东西. 会的同学请看一下指出错误,不会的同学请先自行脑补 ...
随机推荐
- INDEX--从数据存放的角度看索引
测试表结构: CREATE TABLE TB1 ( ID ,), C1 INT, C2 INT ) 1. 聚集索引(Clustered index) 聚集索引可以理解为一个包含表中除索引键外多有剩余列 ...
- Decimal类型截取保留N位小数向上取, Decimal类型截取保留N位小数并且不进行四舍五入操作
Decimal类型截取保留N位小数向上取Decimal类型截取保留N位小数并且不进行四舍五入操作 封装静态方法 public class DecimalHelper { /// <summary ...
- c#中快速排序的学习
最近看了一句话,说的是在现实生活中,会写字的人不见得会写出好文章,学习编程语言就像是学会了写字,学会了编程语言并不一定能写出好程序. 我觉得就是很有道理,以前读书的时候,基本学完了C#中的语法知识,算 ...
- Seaching TreeVIew WPF
项目中有一个树形结构的资源,需要支持搜索功能,搜索出来的结果还是需要按照树形结构展示,下面是简单实现的demo. 1.首先创建TreeViewItem的ViewModel,一般情况下,树形结构都包含D ...
- .NET MVC 学习笔记(四)— 基于Bootstarp自定义弹出框
.NET MVC 学习笔记(四)—— 基于Bootstarp自定义弹出框 转载自:https://www.cnblogs.com/nele/p/5327380.html (function ($) { ...
- 混合式应用开发之AngularJS ng-repeat数组有重复值的解决方法
使用AngularJS ng-repeat遍历数组时,遇到数组里又重复值时会报错.例如数组[1,2,3,3] 官网给了一个解决的方案 <div ng-repeat="value in ...
- “全栈2019”Java多线程第三十四章:超时自动唤醒被等待的线程
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- 【文文殿下】 [USACO08MAR]土地征用 题解
题解 斜率优化裸题. 有个很玄学的事情,就是我用\(f[i]=min\{f[j-1]+p[j].y*p[i].x\}\) 会很奇怪的Wa . 明明和\(f[i]=min\{f[j]+p[j+1].y* ...
- javaweb项目中的过滤器的使用
翻阅博客园的的时候,看到两篇关于javaweb过滤器的帖子写的很好,这里备忘一下: 过滤器基础:http://www.cnblogs.com/xdp-gacl/p/3948353.html 获取器案例 ...
- linux下发送报警邮件(mailx)
本文章主要解决 linux下监控到系统状况后怎么发邮件报警的问题. 如果你是最小化安装的centos/redhat 系统,是没有自带mailx的,也就是没有mail 命令. 解决办法 yum -y i ...