zoukankan      html  css  js  c++  java
  • 拥有自己的代码生成器—NewLife.XCode代码生成器分析

          本博客所有文章分类的总目录:http://www.cnblogs.com/asxinyu/p/4288836.html

    Newlife XCode组件相关文章目录:http://www.cnblogs.com/asxinyu/p/4329747.html

    先说明一下,本文使用的Xcode不是Mac的Xcode,而且Newlife团队开发的一个.NET开发组件。其历史也有将近10年,因此大家不要误会。
    新生命开发团队的相关信息,QQ群:1600800
    博客:http://nnhy.cnblogs.com
    论坛:http://www.53wb.com
     
     前面几篇博客断断续续介绍了一些Newlife.Xcode的使用,特别是在获取数据库架构信息方面,是最有用的。当然Newlife系列组件的强大并不止前面介绍的哪些,更重要的是它能给你带来很多想象和发挥的空间,比如我今天的主题——拥有自己的代码生成器。因为有了模板引擎,Xcoder可以让你省去很多重复的事情,因为Xcode可以很容易的获取到数据库架构的所有信息,所以就可以用来做你想做的事情,很多事情也因此变得简单。越来越发现,写程序原来也是件很艺术的事情,虽然我没有写出那么多艺术的代码。
           这篇博客主要是简单分析Xcoder代码生成器的原理及模板引擎的快速使用方法,了解了这些,拥有自己的代码生成器就很容易。首先来简单看看Xcoder的源码,大概看了下,可能不是那么彻底啊。
    一、Xcoder中的代码生成功能,主要是调用Engine(代码生成器类)来完成核心工作,只不过在界面上获取和设置一些东西,比如命名空间,模板等等。代码生成器工作的时候也是传入相对应的表名称。
    1.有一个常量 TemplatePath = "Template";这个是模板的默认路径,一般是当前应用程序下的Template文件夹
    2.静态属性FileTemplates,会搜索模板目录下的所有模板
    3.Engine初始化时是传入XConfig类,也就是配置信息啊。里面包含了当前代码生成器的一些设置信息,比如界面上的,数据库连接名,命名空间,模板名称,输出目录等。有了这些信息,获取数据库架构信息就很容易了。就不列举了,
    4.最核心的方法就是Render了,由于Xcoder代码生成器主要是针对数据库架构来生成代码,所以一般情况下,要做自己的代码生成器,用Engine就足够了。可是有些情况下,生成一些批量的代码不一定是从数据库来获取元数据的信息,比如我要搞的这个东西,封装WMI中的类,上百个呢(打算做20-30个常用的吧),多不多,但太累,并且是重复的工作,没有技术含量。所以就想办法用代码生成器了,当然需要自己改造下,那接下来就主要看看Render方法里面是怎么调用模板来生成代码的,这个搞明白了,自己也就可以调用XTemplate来生成代码了。
    5.Render方法里面主要就是通过配置信息,来加载模板文件及内容,当然也要搜索模板目录下的所有模板文件,然后放到Dictionary中,这是一个键值对,分别为模板名称和模板内容。然后用这个Dictionary来获得一个Template对象,然后编译模板,关键代码:
     
    1 Template tt = Template.Create(templates);
    2 if (tempName.StartsWith("*")) 
    3         tempName = tempName.Substring(1);
    4 tt.AssemblyName = tempName;
    5 tt.Compile();//编译模板
     二、上面是Xcoder的大概分析,其实Xcoder包括的东西很多,要做代码生成器,肯定要参考这个,里面自动更新,数据库架构信息获取、编辑都有。下面来说说怎么用XTemplate来快速调用模板引擎生成自己所需要的代码。看看我的实际需求,就是上述第4点,我打算封装WMI中的大部分类,有上百个吧,都是大同小异,手写太累,所以就想到用代码生成器,这里和Xcoder唯一的区别是,要生成代码的信息不是数据库架构信息,而是自己手动输入一些元数据信息,比如下面Win32_Keyboard WMI class语法结构:
    View Code
    1 class Win32_Keyboard : CIM_Keyboard
    2 {
    3 uint16 Availability;
    4 string Caption;
    5 uint32 ConfigManagerErrorCode;
    6 boolean ConfigManagerUserConfig;
    7 string CreationClassName;
    8 string Description;
    9 string DeviceID;
    10 boolean ErrorCleared;
    11 string ErrorDescription;
    12 datetime InstallDate;
    13 boolean IsLocked;
    14 uint32 LastErrorCode;
    15 string Layout;
    16 string Name;
    17 uint16 NumberOfFunctionKeys;
    18 uint16 Password;
    19 string PNPDeviceID;
    20 uint16 PowerManagementCapabilities[];
    21 boolean PowerManagementSupported;
    22 string Status;
    23 uint16 StatusInfo;
    24 string SystemCreationClassName;
    25 string SystemName;
    26 };
    就是这个基本结构,但是封装WMI类的时候,参考了Newlife.WMI中的封装,会有属性和字段名称常量等东西。这个类还算比较简单的,就有23个字段,要是长一点50多个字段,那用手写,大家想一下工作量。呵呵。1.知道了需求,看看怎么实现。其实要传入到模板的参数就是上面的类的结构,包括类型和名称,先用一个Dictionary把这些字段信息存储起来,key为字段名称,value为类型。代码如下,我是在Winform中粘贴上述文本,然后程序分析,得到Dictionary。
    View Code
     1 /// <summary>
     2         /// 获取元数据,注意要进行类型转换,value
     3         /// </summary>
     4         /// <returns></returns>
     5         private Dictionary<stringobject> GetData()
     6         {
     7             Dictionary<String, Object> data = new Dictionary<String, Object>();
     8             string[] text = txtOriginText.Text.Trim().Replace("\r\n","").Split(';'); //分割符为;号
     9             foreach (var item in text )
    10             {
    11                 string[] element = item.Trim ().Split(new[]{' '}, StringSplitOptions.RemoveEmptyEntries);
    12                 if (element .Length ==2)
    13                 {
    14                     data.Add(element[1].Trim(), GetNewType (element[0].Trim()));
    15                 }
    16             }
    17             return data;
    18         }
    2.有了这些数据,就可以生成我所需要的代码了吗?是的,千真万确,看看代码,也不得不说一个小问题,考虑时间和代价,这个代码生成并不是十全十美的,因为有一些枚举类型,并没有处理,这样会出错,只能手动修改,当然也可以在模板中处理,只是麻烦一些,为了这5%的事情去浪费95%的精力不值得,手动也快。
    View Code
     1  Dictionary<String, Object> data = GetData();
     2             //将元数据添加到data中去,字段名称作为key,字段类型为 value
     3             //添加命名空间
     4             data.Add("NameSpace", txtNameSpace.Text);
     5             //添加类名称
     6             data.Add("ClassName", txtClassName.Text);                       
     7             //需要读入模板内容
     8             string tempContent = File.ReadAllText("WMI模板.cs");
     9             //调用生成代码,传入模板名称和数据信息Dictionary
    10             string content = Template.ProcessTemplate(tempContent, data);
    11             //获取生成文件的存储地址
    12             String dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,
    13                 txtOutputFolder.Text.Trim (),txtClassName.Text .Trim ()+".cs");
    14             StreamWriter fs = File.CreateText(dir );
    15             fs.Write(content);
    16             fs.Close();
    再来看看,我生成的内容是什么,看看内容吧(模板下一篇再写),这一篇主要介绍模板的调用方法和过程:
    生成的代码太多,我分为2个部分吧:
    View Code
     1 public class Win32_Keyboard :WMIBase
     2     {
     3         #region 字段定义        
     4         private UInt16 _availability ;        
     5         private string _caption ;        
     6         private UInt32 _configmanagererrorcode ;        
     7         private bool _configmanageruserconfig ;        
     8         private string _creationclassname ;        
     9         private string _description ;        
    10         private string _deviceid ;                
    11         private bool _errorcleared ;        
    12         private string _errordescription ;        
    13         private DateTime _installdate ;        
    14         private bool _islocked ;        
    15         private UInt32 _lasterrorcode ;        
    16         private string _layout ;        
    17         private string _name ;        
    18         private UInt16 _numberoffunctionkeys ;        
    19         private UInt16 _password ;        
    20         private string _pnpdeviceid ;        
    21         private UInt16 _powermanagementcapabilities[] ;        
    22         private bool _powermanagementsupported ;        
    23         private string _status ;        
    24         private UInt16 _statusinfo ;        
    25         private string _systemcreationclassname ;        
    26         private string _systemname ;        
    27         private NewLife.WMI.Entities _namespace ;        
    28         private Win32_Keyboard  _classname ;        
    29         private ManagementObject _mo;
    30         #endregion
    31         
    32         #region 初始化
    33         public Win32_Keyboard  (ManagementObject managementObject)
    34         {
    35             this._mo = managementObject;
    36             this.Win32_Keyboard _Init();
    37         }
    38         private void Win32_Keyboard _Init()
    39         {
    40             this._availability = GetPropStr(_mo, "Availability");
    41             this._caption = GetPropStr(_mo, "Caption");
    42             this._configmanagererrorcode = GetPropStr(_mo, "ConfigManagerErrorCode");
    43             this._configmanageruserconfig = GetPropStr(_mo, "ConfigManagerUserConfig");
    44             this._creationclassname = GetPropStr(_mo, "CreationClassName");
    45             this._description = GetPropStr(_mo, "Description");
    46             this._deviceid = GetPropStr(_mo, "DeviceID");
    47             this._errorcleared = GetPropStr(_mo, "ErrorCleared");
    48             this._errordescription = GetPropStr(_mo, "ErrorDescription");
    49             this._installdate = GetPropStr(_mo, "InstallDate");
    50             this._islocked = GetPropStr(_mo, "IsLocked");
    51             this._lasterrorcode = GetPropStr(_mo, "LastErrorCode");
    52             this._layout = GetPropStr(_mo, "Layout");
    53             this._name = GetPropStr(_mo, "Name");
    54             this._numberoffunctionkeys = GetPropStr(_mo, "NumberOfFunctionKeys");
    55             this._password = GetPropStr(_mo, "Password");
    56             this._pnpdeviceid = GetPropStr(_mo, "PNPDeviceID");
    57             this._powermanagementcapabilities[] = GetPropStr(_mo, "PowerManagementCapabilities[]");
    58             this._powermanagementsupported = GetPropStr(_mo, "PowerManagementSupported");
    59             this._status = GetPropStr(_mo, "Status");
    60             this._statusinfo = GetPropStr(_mo, "StatusInfo");
    61             this._systemcreationclassname = GetPropStr(_mo, "SystemCreationClassName");
    62             this._systemname = GetPropStr(_mo, "SystemName");
    63             this._namespace = GetPropStr(_mo, "NameSpace");
    64             this._classname = GetPropStr(_mo, "ClassName");
    65         }
    66         #endregion
    第二个部分,和上面是一个文件啊:
    View Code
    1 #region 属性-默认只读,其他手动修改
    2
    3 ///<summary></summary>
    4 public UInt16 Availability
    5 {
    6 get{ return this._availability;}
    7 }
    8
    9 ///<summary></summary>
    10 public string Caption
    11 {
    12 get{ return this._caption;}
    13 }
    14
    15 ///<summary></summary>
    16 public UInt32 ConfigManagerErrorCode
    17 {
    18 get{ return this._configmanagererrorcode;}
    19 }
    20
    21 ///<summary></summary>
    22 public bool ConfigManagerUserConfig
    23 {
    24 get{ return this._configmanageruserconfig;}
    25 }
    26
    27 ///<summary></summary>
    28 public string CreationClassName
    29 {
    30 get{ return this._creationclassname;}
    31 }
    32
    33 ///<summary></summary>
    34 public string Description
    35 {
    36 get{ return this._description;}
    37 }
    38
    39 ///<summary></summary>
    40 public string DeviceID
    41 {
    42 get{ return this._deviceid;}
    43 }
    44
    45 ///<summary></summary>
    46 public bool ErrorCleared
    47 {
    48 get{ return this._errorcleared;}
    49 }
    50
    51 ///<summary></summary>
    52 public string ErrorDescription
    53 {
    54 get{ return this._errordescription;}
    55 }
    56
    57 ///<summary></summary>
    58 public DateTime InstallDate
    59 {
    60 get{ return this._installdate;}
    61 }
    62
    63 ///<summary></summary>
    64 public bool IsLocked
    65 {
    66 get{ return this._islocked;}
    67 }
    68
    69 ///<summary></summary>
    70 public UInt32 LastErrorCode
    71 {
    72 get{ return this._lasterrorcode;}
    73 }
    74
    75 ///<summary></summary>
    76 public string Layout
    77 {
    78 get{ return this._layout;}
    79 }
    80
    81 ///<summary></summary>
    82 public string Name
    83 {
    84 get{ return this._name;}
    85 }
    86
    87 ///<summary></summary>
    88 public UInt16 NumberOfFunctionKeys
    89 {
    90 get{ return this._numberoffunctionkeys;}
    91 }
    92
    93 ///<summary></summary>
    94 public UInt16 Password
    95 {
    96 get{ return this._password;}
    97 }
    98
    99 ///<summary></summary>
    100 public string PNPDeviceID
    101 {
    102 get{ return this._pnpdeviceid;}
    103 }
    104
    105 ///<summary></summary>
    106 public UInt16 PowerManagementCapabilities[]
    107 {
    108 get{ return this._powermanagementcapabilities[];}
    109 }
    110
    111 ///<summary></summary>
    112 public bool PowerManagementSupported
    113 {
    114 get{ return this._powermanagementsupported;}
    115 }
    116
    117 ///<summary></summary>
    118 public string Status
    119 {
    120 get{ return this._status;}
    121 }
    122
    123 ///<summary></summary>
    124 public UInt16 StatusInfo
    125 {
    126 get{ return this._statusinfo;}
    127 }
    128
    129 ///<summary></summary>
    130 public string SystemCreationClassName
    131 {
    132 get{ return this._systemcreationclassname;}
    133 }
    134
    135 ///<summary></summary>
    136 public string SystemName
    137 {
    138 get{ return this._systemname;}
    139 }
    140
    141 ///<summary></summary>
    142 public NewLife.WMI.Entities NameSpace
    143 {
    144 get{ return this._namespace;}
    145 }
    146
    147 ///<summary></summary>
    148 public Win32_Keyboard ClassName
    149 {
    150 get{ return this._classname;}
    151 }
    152
    153 #endregion
    154
    155 #region 获取/设置 字段值
    156 ///<summary>
    157 /// 获取/设置 字段值。
    158 /// 一个索引,基类使用反射实现。
    159 /// 派生实体类可重写该索引,以避免反射带来的性能损耗
    160 ///</summary>
    161 ///<param name="name">字段名</param>
    162 public Object this[String name]
    163 {
    164 get{
    165 switch (name)
    166 {
    167 case "Availability" : return _availability;
    168 case "Caption" : return _caption;
    169 case "ConfigManagerErrorCode" : return _configmanagererrorcode;
    170 case "ConfigManagerUserConfig" : return _configmanageruserconfig;
    171 case "CreationClassName" : return _creationclassname;
    172 case "Description" : return _description;
    173 case "DeviceID" : return _deviceid;
    174 case "ErrorCleared" : return _errorcleared;
    175 case "ErrorDescription" : return _errordescription;
    176 case "InstallDate" : return _installdate;
    177 case "IsLocked" : return _islocked;
    178 case "LastErrorCode" : return _lasterrorcode;
    179 case "Layout" : return _layout;
    180 case "Name" : return _name;
    181 case "NumberOfFunctionKeys" : return _numberoffunctionkeys;
    182 case "Password" : return _password;
    183 case "PNPDeviceID" : return _pnpdeviceid;
    184 case "PowerManagementCapabilities[]" : return _powermanagementcapabilities[];
    185 case "PowerManagementSupported" : return _powermanagementsupported;
    186 case "Status" : return _status;
    187 case "StatusInfo" : return _statusinfo;
    188 case "SystemCreationClassName" : return _systemcreationclassname;
    189 case "SystemName" : return _systemname;
    190 case "NameSpace" : return _namespace;
    191 case "ClassName" : return _classname;
    192 default: return "" ;
    193 } }
    194 }
    195 #endregion
    196
    197 #region 字段名
    198 public class _
    199 {
    200 ///<summary></summary>
    201 public static readonly string Availability = "Availability";
    202 ///<summary></summary>
    203 public static readonly string Caption = "Caption";
    204 ///<summary></summary>
    205 public static readonly string ConfigManagerErrorCode = "ConfigManagerErrorCode";
    206 ///<summary></summary>
    207 public static readonly string ConfigManagerUserConfig = "ConfigManagerUserConfig";
    208 ///<summary></summary>
    209 public static readonly string CreationClassName = "CreationClassName";
    210 ///<summary></summary>
    211 public static readonly string Description = "Description";
    212 ///<summary></summary>
    213 public static readonly string DeviceID = "DeviceID";
    214 ///<summary></summary>
    215 public static readonly string ErrorCleared = "ErrorCleared";
    216 ///<summary></summary>
    217 public static readonly string ErrorDescription = "ErrorDescription";
    218 ///<summary></summary>
    219 public static readonly string InstallDate = "InstallDate";
    220 ///<summary></summary>
    221 public static readonly string IsLocked = "IsLocked";
    222 ///<summary></summary>
    223 public static readonly string LastErrorCode = "LastErrorCode";
    224 ///<summary></summary>
    225 public static readonly string Layout = "Layout";
    226 ///<summary></summary>
    227 public static readonly string Name = "Name";
    228 ///<summary></summary>
    229 public static readonly string NumberOfFunctionKeys = "NumberOfFunctionKeys";
    230 ///<summary></summary>
    231 public static readonly string Password = "Password";
    232 ///<summary></summary>
    233 public static readonly string PNPDeviceID = "PNPDeviceID";
    234 ///<summary></summary>
    235 public static readonly string PowerManagementCapabilities[] = "PowerManagementCapabilities[]";
    236 ///<summary></summary>
    237 public static readonly string PowerManagementSupported = "PowerManagementSupported";
    238 ///<summary></summary>
    239 public static readonly string Status = "Status";
    240 ///<summary></summary>
    241 public static readonly string StatusInfo = "StatusInfo";
    242 ///<summary></summary>
    243 public static readonly string SystemCreationClassName = "SystemCreationClassName";
    244 ///<summary></summary>
    245 public static readonly string SystemName = "SystemName";
    246 ///<summary></summary>
    247 public static readonly string NameSpace = "NameSpace";
    248 ///<summary></summary>
    249 public static readonly string ClassName = "ClassName";
    250 }
    251 #endregion
     
    上面是主要代码,下面看看我用主要代码做的一个简单界面,以及运行时候的效果。下一篇将写一下,上面代码生成的模板如何写,以及要注意的事项。
    我用Winform做的WMI代码生成界面,比较丑陋啊,但是功能不减,呵呵。
     
     

    --
    呵呵,到此为止。
     
     
  • 相关阅读:
    钩子函数和回调函数的区别
    观察者模式(Observer)和发布-订阅者模式(Publish/Subscribe)区别
    前端解决跨域问题的终极武器——Nginx反向代理
    CORS(cross-origin-resource-sharing)跨源资源共享
    Vue父子组件通讯
    js的变量——基本类型保存在栈中,引用类型保存在堆中
    python
    CentOS7 下 Zabbix3.4 源码安装
    linux配置ssh公钥认证,打通root用户的免密码输入的scp通道
    python
  • 原文地址:https://www.cnblogs.com/asxinyu/p/2492817.html
Copyright © 2011-2022 走看看