七、CodeSmith控制台指南。
很多人仅仅知道CodeSmith像一个图形应用程序,或者可能是一个Visual Studio的附件,但是通过CodeSmith的控制台应用程序还有好多其他的使用方法。控制台应用程序是很有价值的,因为可以通过它去生成脚本,或者其他一些自动工具。这篇文档的目的就是要告诉你怎样使用它的控制台应用程序并且如何去定义变量和参数。
Basic Usage
大多数情况下是用控制台应用程序来创建一个模板,一个属性文件,然后保存输出的文件。这有一个很好的例子介绍将合并模版的处理过程放到一个过程中,就像使用NAnt工具。
首先我们要确定完成一个什么样的模版,为这个模板创建一个什么样的XML属性文件。XML属性文件提供在执行模版是需要的各个属性。生成一个属性文件最简单的方法是在CodeSmith Explorer中打开一个模版,填写属性,点击生成按钮generate,然后再点击Save Property Set XML按钮。这个按钮会在点击完生成按钮后找到,在Save Output和Copy Output按钮旁边。然后系统提示输入保存XML属性文件的文件名,下面看一个ArrayList.cst模版创建的XML属性文件。
2 < codeSmith >
3 < propertySet >
4 < property name ="Accessibility" > Public </ property >
5 < property name ="ClassName" > PersonArray </ property >
6 < property name ="ItemType" > Person </ property >
7 < property name ="ItemValueType" > False </ property >
8 < property name ="ItemCustomSearch" > False </ property >
9 < property name ="KeyName" > PersonID </ property >
10 < property name ="KeyType" > int </ property >
11 < property name ="IncludeInterfaces" > True </ property >
12 < property name ="IncludeNamespaces" > False </ property >
13 </ propertySet >
14 </ codeSmith >
就像看到的一样,也可以手动创建这个文件,但是使用CodeSmith Explorer会更简便。
现在我们有了这个XML文件,我们继续看一下如何去执行这个模版并是用控制台工具保存结果。首先我们需要是用/template参数去声明我们要是用的模版,像这样:
C:/Program Files/CodeSmith/v3.0>cs /template:Samples/Collections/ArrayList.cst
在这个例子中我们使用了ArrayList.cst模版,它存储在本地的Samples/Collections文件夹下。下一步我们要去声明我们在最后一步需要创建的XML文件,我们是用/propertyset参数去实现。
C:/Program Files/CodeSmith/v3.0>cs /template:Samples/Collections/ArrayList.cst /propertyset:PersonArray.xml
这个/property参数用来指定我们的XML属性文件。最后一个我们需要用的参数是/output参数,用来指定输出怎样被保存。
C:/Program Files/CodeSmith/v3.0>cs /template:Samples/Collections/ArrayList.cst /propertyset:PersonArray.xml /out:test.cs
使用/out参数指定将结果输出到一个叫test.cs文件中保存。执行这个命令后,模板将开始运行,使用属性文件将结果输出到test.cs文件保存。
这是大多数情况下有效使用控制台。
Merging Output
在各种代码生成中最大的挑战就是将生成的代码和开发人员编写或修改的代码区分开。控制台对这个问题提供了一个有效的独特的解决方案,使用一个指定的参数在当前已存在的代码文件中需要将模板生成的代码添加的地方指定一块区域。
下面是一个简单的代码文件,包含了我们要添加生成代码的区域。
2
3 namespace Entities
4 {
5 GeneratedOrderEntity#region GeneratedOrderEntity
6
7
8 #endregion
9}
我们的目标是将DatabaseSchema/BusinessObject.cst模版生成的代码添加到类文件的GeneratedOrderEntity区域中。和上一个例子一样,使用CodeSmith console控制台应用程序执行这个模版,但是这次要使用另一个参数merge。
C:/Program Files/CodeSmith/v3.0>cs /template:Samples/DatabaseSchema/BusinessObject.cst /propertyset:OrderEntity.xml /out:OrderEntity.cs /merge:InsertRegion= "RegionName=Sample Generated Region;Language=C#;"
使用merge参数我们可以指定区域的名称,在这个例子中是GeneratedOrderEntity,然后控制台应用程序将执行模版,并将结果添加到这个区域中。我们来看一下执行完这个指令后生成的代码。
2
3 namespace Infozerk.AuthServices.UnitTestSuite
4 {
5 GeneratedOrderEntity#region GeneratedOrderEntity
6
7
8 Order#region Order
9 /**//// <summary>
10 /// http://www.livebaby.cn.
11 /// </summary>
12 public class Order
13 {
14 protected int _id;
15 protected string _customerID = String.Empty;
16 protected int _employeeID;
17 protected DateTime _orderDate;
18 protected DateTime _requiredDate;
19 protected DateTime _shippedDate;
20 protected int _shipVia;
21
22--为了简短省略了类的其他部分 就像看到的一样,Order类被添加到了我们指定的区域中。在代码文件中使用merge参数生成的内容在其他部分被修改或手写后很容易重新再次生成而不会产生影响。
参数介绍Parameter Reference
Specifying Output
/out:<file>
指定从模版创建的输出文件的名称。
/out:default
指定这个文件被默认保存成模版是用的名称。
/merge:<mergetype>=<init>
指定模版输出的区域。可以简写为/m
Specifying Input
/template:<file>
选择要执行的模版,简写为/t
/propertyset:<file>
生成代码时需要使用的XML属性文件。简写为/p
Compiler Options
/debug[+|-]
指定模版需要包含的调试信息。(允许在运行模版时进行调试)
/tempfiles[+|-]
指定保留临时文件。(如果在临时文件上调试也可以)
Miscellaneous
/help
显示帮助信息。
/nologo
禁止生成器版权信息。
八、编写CodeSmith自定义属性的编辑器(Writing Custom Property Editors)
当你开始编写自定义的CodeSmith模板时,很可能对于使用它的strings或integers属性很满意,但有时你会发现需要创建一个不同类型的属性,可能是一个自定义的类型或者是.NET framework中但是在属性面板中没有提供的类型。在模板中去作这些很简单,但是怎样指定一个类型在运行模板时显示在属性面板中呢?例如创建了一个Person类并且具有很多不同的属性,但是却没有办法让用户去组装这个类……除非创建一个自定义属性编辑器。
属性面板提供了方法去编写自定义的属性编辑器,当用户在面板上选择一个属性时可以激发相应的方法,同时也可以通过编写代码实现提示用户输入一个必要的值。下面我们举个例子,创建一个接受组建的属性并且是用影射循环贯串所有的类,是所有的类都可以使用它和它的方法,去创建一个NUnit测试基础。(这句翻译的不好,原文:As an example we are going to build a template which accepts an assembly as a property and then using reflection loops through all of the classes, and the methods of those classes, to build NUnit test stubs.)
首先,我们来关注一下模板的组件属性,暂且不看自定义编写的代码。模板的第一部分是一些声明定义和属性。将属性放在脚本标签中代替使用属性声明,在下一部分将看到这样做的必要。
2
3 <% @ Import NameSpace = " System.Reflection " %>
4
5 < script runat = " template " >
6
7 private Assembly assembly;
8
9 public Assembly AssemblyToLoad
10 {
11 get{return assembly;}
12 set{assembly = value;}
13}
14
15 </ script >
然后我们为组建assembly中的每一个类创建一个类,为每一个类创建他的方法。然后直接将模板的输出内容放入Visual Studio.NET,然后在编写组件的单元测试时使用向导。
2 using NUnit.Framework;
3
4 <%
5 foreach (Type T in AssemblyToLoad.GetTypes())
6 {
7 if(T.IsClass)
8 {
9 %>
10
11 [TestFixture]
12 public class <%=T.Name%>Tests
13 {
14 <%
15 MethodInfo[] methods = T.GetMethods ( BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static );
16 foreach(MethodInfo M in methods)
17 {
18 %>
19
20 [Test]
21 public void <%=M.Name%>Test
22 {
23 //TODO Write this test
24 }
25 <%
26 }
27
28 %>}<%
29 }
30 }
31 %>
/Files/Bear-Study-Hard/AssemblyHelper.zip
首先我们需要创建一个继承UITypeEditor的类。
2 {
3 public AssemblyFilePicker(): base()
4 {
5 }
6}
关于UITypeEditor的说明请大家参看M SD N或Visual Studio.NET自带帮助中的说明,其中有详细的例子。
然后我们需要重载UITypeEditor类的两个不同的方法。第一个需要重载点的方法是GetEditStyle,这个方法是告诉属性面板对于当前类型是用什么类型的编辑器,在这个例子中我们设置编辑类型为Modal。这样大家可以在该属性格子的右边看到一个小按钮,它将引发一个对话框等模式的对话(trigger a modal dialog)。这是我们的GetEditStyle方法:
2 {
3 return UITypeEditorEditStyle.Modal;
4}
其中的Modal为显示一个省略号按钮。
首先我们要从当前的服务和控件中得到一个参考,有了控件的参考我们可以通过它转到ShowDialog方法。(原文:First we need to get a reference to the current service and control, we need the reference to the control so we can pass it to the ShowDialog method.) 然后我们创建一个openFileDialog类并填入适合的属性。 然后我们通过控件的参考(reference)将对话框显示给用户。 下一步我们检查用户是否点击了OK按钮,如果点击了,通过文件选择对话框选择文件后使用LoadForm方法加载这个组件,最后返回这个值。 这个值将被放在属性面板中并可以被模板读取,但是需要注意,在我们作这个之前要将组件import引入到模板中,并在模板中用一对属性声明。
2 Control editorControl = editorService as Control;
3
4 if (editorControl != null )
5 {
2
3 openFileDialog.CheckFileExists = true ;
4 openFileDialog.DefaultExt = " .dll " ;
5 openFileDialog.Multiselect = false ;
6 openFileDialog.Title = " Select an Assembly: " ;
7 openFileDialog.Filter = " Assembly Files | *.dll " ;
2 {
3Assembly assembly = Assembly.LoadFrom( openFileDialog.FileName ) ;
4 value = assembly;
5 }
6 else
7 {
8 value = null;
9 }
10 }
11 }
12
13 return value;
14 }
加载这个模板我们仅需将这个组件assembly与模板放在同一目录下,然后再模板中加入下面两行代码。
2 < %@ Import NameSpace ="AssemblyHelper" % >
需要重载的另一个方法是EditValue方法,当用户电击属性时会调用这个方法。按照我们需要加载的组件类型需要创建一个打开文件对话框(open file dialog)然后捕获这个对话框,在属性格子中返回对话框的结果。
2 {
3
4if (provider != null)
5{
这个模板仅仅可以编译通过,但是由于我们编写显示了一个类型属性面板并不知道如何去操作它,所以我们没有办法自定义指定组件在加载时想要加载的组件。
我们需要创建一个UITypeEditor,这是一个建立属性面板是用的特殊属性的类。UITypeEditor需要创建在一个和模板分离的组件中,我们是用Visual Studio创建这个类。