代码生成器的原理无非就是得到字段相关信息(字段名,字段类型,字段注释等),然后根据模板,其实就是字符串的拼接与替换生成相应代码。
所以第一步我们需要解决如何得到字段的相关信息,有两种方式
通过反射获得程序集类的字段相关信息
读取数据库得到表的字段的相关信息
新建一个.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>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>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>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>The type or parent type, used to fetch the assembly</param> /// <param>The prefix as seen in the name attribute in the documentation XML</param> /// <param>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>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>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>The error message that explains the reason for the exception.</param> /// <param>The exception that is the cause of the current exception, or null if none.</param> public DocsByReflectionException(string message, Exception innerException) : base(message, innerException) { } }