拥有自己的代码生成器—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代码生成界面,比较丑陋啊,但是功能不减,呵呵。
 
 

--
呵呵,到此为止。
 
 
posted @ 2012-05-09 21:45  数据之巅  阅读(5967)  评论(7编辑  收藏  举报