• 手把手教你实现自己的abp代码生成器


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

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

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

      1531110372710

    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相关信息,报如下错误

      1531114232956

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

      1531114396512

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

      1531114809200

    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: [
                      {
                           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

  • 相关阅读:
    Sokect简单入门(1)TCP协议一
    Tomcat内存溢出 解决方法
    服务器配置 ssl 证书,Nginx配置
    优秀的博客文章记录
    SpringBoot实现优雅的关机
    关于 redis 的 数据类型 和 内存模型
    微信, qq 支付宝 等相关开发 资源 记录
    设计模式 之 单列设计模式
    Swagger
    MQ服务器奔溃解决过程
  • 原文地址:https://www.cnblogs.com/aishangyipiyema/p/9293651.html
Copyright © 2020-2023  润新知