拥有自己的代码生成器—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语法结构:
拥有自己的代码生成器—NewLife.XCode代码生成器分析 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。
拥有自己的代码生成器—NewLife.XCode代码生成器分析 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%的精力不值得,手动也快。
拥有自己的代码生成器—NewLife.XCode代码生成器分析 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个部分吧:
拥有自己的代码生成器—NewLife.XCode代码生成器分析 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
第二个部分,和上面是一个文件啊:
拥有自己的代码生成器—NewLife.XCode代码生成器分析 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代码生成界面,比较丑陋啊,但是功能不减,呵呵。
 
拥有自己的代码生成器—NewLife.XCode代码生成器分析
 
拥有自己的代码生成器—NewLife.XCode代码生成器分析

--
呵呵,到此为止。
 
 

你可能感兴趣的:(xcode)