代码生成器的原理无非就是得到字段相关信息(字段名,字段类型,字段注释等),然后根据模板,其实就是字符串的拼接与替换生成相应代码。

所以第一步我们需要解决如何得到字段的相关信息,有两种方式

  • 通过反射获得程序集类的字段相关信息
  • 读取数据库得到表的字段的相关信息
  1. 新建一个.NET Core控制台项目 取名AbpCodeGenerator

  2. 新建类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)
    { }
    }
  3. 新建类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;
    } }
  4. 在Program类中添加如下代码

    //反射程序集的方式生成相应代码
    string className = "User";//跟类名保持一致
    var metaTableInfoList = MetaTableInfo.GetMetaTableInfoListForAssembly(className);

    启动控制台,获得User相关信息,报如下错误

    这是缺少ABP相关程序集引用,使用NuGet安装如下程序集

  5. 启动控制台得到如下结果,剩下的就是字符串拼接了

  6. 新建一个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}}();
    }
    }); });
    })();
  7. 首先读取文件模板,然后去替换掉“{{}}” 这种类似占位符的变量,然后再输出一个新的文件,代码如下

     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
    }
  8. 启动控制台,即在当前项目下生成相应代码,这里分享一个我之前写好的基于abp.zero core 5.3.0的

    https://github.com/HisKingdom/CodeGenerator

手把手教你实现自己的abp代码生成器的更多相关文章

  1. 手把手教你用动软.NET代码生成器实例教程

    动软实战攻略 手把手教你用动软 文档编号:20110421 版权所有 © 2004-2011 动软 在线帮助:http://help.maticsoft.com 目录   一.        产品介绍 ...

  2. 手把手教你使用FineUI+动软代码生成器开发一个b/s结构的取送货管理信息系统(附源码)之开篇

    一 本系列随笔概览及产生的背景 近阶段接到一些b/s类型的软件项目,但是团队成员之前大部分没有这方面的开发经验,于是自己选择了一套目前网上比较容易上手的开发框架(FineUI),计划录制一套视频讲座, ...

  3. 手把手教你使用FineUI开发一个b/s结构的取送货管理信息系统系列博文索引

    近阶段接到一些b/s类型的软件项目,但是团队成员之前大部分没有这方面的开发经验,于是自己选择了一套目前网上比较容易上手的开发框架(FineUI),计划录制一套视频讲座,来讲解如何利用FineUI快速开 ...

  4. 手把手教你做个人 app

    我们都知道,开发一个app很大程度依赖服务端:服务端提供接口数据,然后我们展示:另外,开发一个app,还需要美工协助切图.没了接口,没了美工,app似乎只能做成单机版或工具类app,真的是这样的吗?先 ...

  5. 手把手教从零开始在GitHub上使用Hexo搭建博客教程(四)-使用Travis自动部署Hexo(2)

    前言 前面一篇文章介绍了Travis自动部署Hexo的常规使用教程,也是个人比较推荐的方法. 前文最后也提到了在Windows系统中可能会有一些小问题,为了在Windows系统中也可以实现使用Trav ...

  6. 手把手教从零开始在GitHub上使用Hexo搭建博客教程(三)-使用Travis自动部署Hexo(1)

    前言 前面两篇文章介绍了在github上使用hexo搭建博客的基本环境和hexo相关参数设置等. 基于目前,博客基本上是可以完美运行了. 但是,有一点是不太好,就是源码同步问题,如果在不同的电脑上写文 ...

  7. 手把手教从零开始在GitHub上使用Hexo搭建博客教程(二)-Hexo参数设置

    前言 前文手把手教从零开始在GitHub上使用Hexo搭建博客教程(一)-附GitHub注册及配置介绍了github注册.git相关设置以及hexo基本操作. 本文主要介绍一下hexo的常用参数设置. ...

  8. 手把手教从零开始在GitHub上使用Hexo搭建博客教程(一)-附GitHub注册及配置

    前言 有朋友问了我关于博客系统搭建相关的问题,由于是做开发相关的工作,我给他推荐的是使用github的gh-pages服务搭建个人博客. 推荐理由: 免费:github提供gh-pages服务是免费的 ...

  9. UWP Jenkins + NuGet + MSBuild 手把手教你做自动UWP Build 和 App store包

    背景 项目上需要做UWP的自动安装包,在以前的公司接触的是TFS来做自动build. 公司要求用Jenkins来做,别笑话我,之前还真不晓得这个东西. 会的同学请看一下指出错误,不会的同学请先自行脑补 ...

随机推荐

  1. 用JavaScript写的动态表格

    实现的功能有Table表格添加,删除.输入,删除的全选,单行删除. HTML代码部分 <body> <form> <table border="1" ...

  2. 使用UTL_HTTP时遭遇ORA-29273

    http://blog.itpub.net/8520577/viewspace-1295182/ 项目中需要使用utl_http访问webserivce.使用utl_http时报错ORA-29273. ...

  3. java 注释annotation学习---两篇不错的blog

    深入理解Java:注解(Annotation)自定义注解入门 ---- http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html 深入理 ...

  4. Apollo 配置详细步骤(Windows环境)

    一. 准备工作 1.下载 apollo 安装包 下载链接:http://activemq.apache.org/apollo/download.html 2.下载 JavaJDK 安装包 ( apol ...

  5. python使用httplib2访问REST服务的例子

    首先你需要安装httplib2,这个可以在github上找到: 然后你需要获得一个http连接的对象: con = httplib2.Http()   然后你需要发起连接: (6)resp, (5)c ...

  6. 一个简单的用python 实现系统登录的http接口服务实例

    用python 开发一个登录的http接口: 用户登录数据存在缓存redis里,登录时根据session判断用户是否已登录,session有效,则直接返回用户已登录,否则进mysql查询用户名及密码, ...

  7. 最小割(zjoi2011,bzoj2229)(最小割树)

    小白在图论课上学到了一个新的概念--最小割,下课后小白在笔记本上写下了如下这段话: "对于一个图,某个对图中结点的划分将图中所有结点分成两个部分,如果结点\(s,t\)不在同一个部分中,则称 ...

  8. C#6.0语言规范(七) 表达式

    表达式是运算符和操作数的序列.本章定义了操作数和运算符的语法,求值顺序以及表达式的含义. 表达式分类 表达式分类为以下之一: 一个值.每个值都有一个关联的类型. 一个变量.每个变量都有一个关联的类型, ...

  9. 多个SpringMVC项目配置统一管理(来自于springCloud的统一配置思路)

    因公司项目分多个系统进行开发,而系统架构几乎完全一样,所以同样的配置文件会存在不同的系统中 当其中的某些配置需要修改时,就需要依次把所有系统中相关的配置都修改掉 纯耗时且没技术含量的体力活 所以借鉴S ...

  10. vue.js 的起步

    vue.js 的起步 转载 作者:伯乐在线专栏作者 - 1000copy 点击 → 了解如何加入专栏作者 如需转载,发送「转载」二字查看说明 介绍 vue.js 是一个客户端js库,可以用来开发单页应 ...