CodeSmith快速向导本文档的目的就是让你尽快得知道怎么通过CodeSimth从一个集合中产生你需要的完整程序。在本向导里,我打算教你使用CodeSmith自带的模版,如何创建一个模版以及如何创建一个数据库驱动模版。 CodeSimth是一个基于模版基础的代码生成工具,通过使用一个类似Asp.net的语法你可以创建和定义一个用于产生代码的模版。你应该知道的一个重要的事情是:CodeSimth产生的代码是文本的,因此你可以用它产生那些你没有必要编写的其他语言的代码或者文本。 在看如何创建你自己的模版之前,让我们先用下CodeSmith自带的模版.你可以通过:开始->程序->CodeSmith->CodeSmith Explorer 来启动CodeSmith Explorer.启动后的界面如下图所示: CodeSmith Explorer用来现实你可以使用的模版;默认的示例模版是被载入的。许多常用的模版在集合顶部,双击ArArrayList.cst模版打开,将看到如下图所示: 这是一个模版属性面板,你可以查看和指定多个模版的属性。对于ArrayList模版我们需要指定我们想创建的ArrayList的名称和我们想存储在ArrayList内item的类型。(我们也可以随意指定一个命名空间以使用ArrayList)本例子里我们指定CarList作为ClassName,Car作为ItemType.添好属性之后单击Generate,模版将为你生成一个强类型的ArrayList。(译者注:我的CodeSmith v 2.6 professional 版点了Generate后没反应,你可以到CodeSmith Studio 里面试) 右面的窗口显示了使用为Car类生成一个强类型ArrayList模版后的输出结果,接着你可以把他拷贝和复制到Visual Studio总,或者其他你用的编辑器中编译它。 (译者注:我的CodeSmith v 2.6 professional 版 点了Generate后没反应,你可以到CodeSmith Studio 里面试。开始-〉程序->CodeSimith v2.6->CodeSmith Studio,右边面板,展开Collections->双击ArrayList.cst,填入下面Properties面版里面的ClassName和ItemType值.->点工具栏里的运行(小三角形)) 编写你的第一个模版CodeSmith和其他的代码生成工具相比较,最有价值的是:你可以通过编写你需要生成的代码的代码生成模版,你可以规定它代码如何生成,甚至指定在代码里面tabs和空格的数量。你可以完全控制你的代码输出,其他的工具几乎不可能完成这样的功能。 你用来生成模版的工具已经有一堆不同的功能,如果你正在使用个人版的CodeSmith,你可以使用CodeSmith Studio,它有丰富的CodeSmith模版环境。如果你没有使用个人版或者试用过期,那么你可以使用任何的文本编辑器创建你的模版。 下面我们将创建一个简单的模版,它接受一些字符串然后创建一个包含了特殊注释头的类。这是一个简单的例子,但它将展示CodeSmith模版创建的基础。我发现设计一个模版最好的方式是首先创建一个模版的需要输出结果的代码,下面个就是模版将输出的内容:
///// File: MyClass.cs// Description: Enter summary here after generation.// ---------------------// Copyright ? 2003 Our Client// ---------------------// History// 11/30/2003 Developer's Name Original Version/// using System; namespace MyNamespace{ /// <summary> /// Summary description for MyClass. /// </summary> public class MyClass { public MyClass() { // // TODO: Add constructor logic here // } }}
这就是我们想模版最终生成的结果,不过我们需要在模版每次运行时指定一些东西。那么现在让我们来开始编写模版吧。 首先打开一个空白的文本文件,并在文件头部增加一个CodeTemplate的标签。CodeTemplate 标签告诉CodeSmith这是一个模版文件,和模版用什么语言编写的,模版生成的语言以及模版描述。下面是一个CodeTemplate标签:<%@ CodeTemplate Language="C#" TargetLanguage="C#" Description="Generates a class including a special informational header" %> 首先我们设置模版语言为C#,接着设置模版生成的语言为C#,然后添加模版描述。还有一些其它的属性可以在标签中增加,它将在文档其他的地方被代替。 接着声明模版在运行时候需要的变量。本例模版中想指定命名空间的名称,类名和开发者的名称。下面是三个需要声明的属性: <%@ Property Name="NameSpace" Type="String" Category="Context" Description="The namespace to use for this class" %> <%@ Property Name="ClassName" Type="String" Category="Context" Description="The name of the class to generate" %> <%@ Property Name="DevelopersName" Type="String" Category="Context" Description="The name to include in the comment header" %> 每个属性标签包括Name属性,它被用来做模版其它地方的属性的值和属性类型的参考。我们已经为每个属性设置了一个类别和描述,这些在模版被CodeSmith打开的时候可以用来定义相关的属性。 接着修改模版文本以便当属性被指定时候文本文字可以被插入到正确位置。你可以使用Asp.Net类似的语法和属性名称来操作,比如:///// File: <%=ClassName%>.cs 值将被文本替代,正如你说见语法和Asp.net类似。我们想看到我们所希望的剩余部分和用属性名称替换硬编码的。这个实例模版全部内容:<%@ CodeTemplate Language="C#" TargetLanguage="C#" Description="Generates a class including a special informational header" %> <%@ Property Name="NameSpace" Type="String" Category="Context" Description="The namespace to use for this class" %> <%@ Property Name="ClassName" Type="String" Category="Context" Description="The name of the class to generate" %> <%@ Property Name="DevelopersName" Type="String" Category="Context" Description="The name to include in the comment header" %> ///// File: <%=ClassName%>.cs// Description: Enter summary here after generation.// ---------------------// Copyright ? <%= DateTime.Now.Year %> Our Client// ---------------------// History// <%= DateTime.Now.ToShortDateString() %> <%= DevelopersName%> Original Version/// using System; namespace <%=NameSpace %>{ /// <summary> /// Summary description for <%=ClassName %>. /// </summary> public class <%=ClassName %> { public <%=ClassName %>() { // // TODO: Add constructor logic here // } }}正如你所看到的我们在几个不同的地方应用了我们定义的那些属性,我们也使用了DateTime对象去为我们显示一个时间。现在我们需要在CodeSmith explorer中调用这个模板,运行你的CodeSmith explorer,单击文件夹图标选择模板文件所在的目录。你会看到目录下的所有模板列表,接着双击模板载入他。模板载入之后我们可以在面板上看到他的属性列表,如图:可以看到我们声明的三个属性,包括类别说明和描述。当你单击生成按钮后,CodeSmith使用指定的属性和我们的模板代码生成下列的内容。///// File: MyClass.cs// Description: Enter summary here after generation.// ---------------------// Copyright ? 2003 Our Client// ---------------------// History// 12/2/2003 Mr. Smith Original Version/// using System; namespace MyNameSpace{ /// <summary> /// Summary description for MyClass. /// </summary> public class MyClass { public MyClass() { // // TODO: Add constructor logic here // } }}
你可以把生成的代码拷粘贴到Visual Studio或其他你使用的编译器中,然后编译他们。这是一个CodeSmith可以做什么的最简单的例子,但列举了一些要点:。模板应该包括一个CodeTemplate声明;。模板可以有很多个属性和用属性声明的被声明的属性。。使用asp.net语法把属性值插入到输出的模板中。模板是100%定制的,你可以用CodeSmith生成任何种类的文本。可是上面的例子还不能真正的展示CodeSmith的强大能力,下节中我们会看到怎么样基于数据库对象动态的生成代码。编写一个数据库驱动的模板现在我们已经认识了哪些怎么样使用CodeSmith配合我们工作的要素,我们接着学如何生成你可能下载CodeSmith首先想要生成的东西。数据访问逻辑可能是程序中最多余的部分,使用CodeSmith你可以自动生成相当正规的数据访问层代码。最后章节你可以看到如何编写一个简单的支持参数的模板,但是现在我们打算看看如何通过使用CodeSmith的SchemaExplorer组件编写模板。SchemaExplorer组件是一个程序集,它提供了多个可以用来浏览你数据库内容的类。通过使用SchemaExplorer组件你可以浏览表和存储过程以及取到数据类型,唯一列,列名等信息。作为一个如何使用SchemaExplorer的例子,我们打算编写一个根据表列自动生成存储过程的模板。开始编写模板前我们应该编写一个希望模板生成的文本内容,这样我们可以使用这些文本作为模板的开始。下面是我们期望生成的文本:------------------------------------------------------------------- Date Created: Thursday, December 04, 2003-- Created By: Generated by CodeSmith----------------------------------------------------------------- CREATE PROCEDURE dbo.UpdateOrders @OrderID int, @CustomerID nchar(5), @EmployeeID int, @OrderDate datetime, @RequiredDate datetime, @ShippedDate datetime, @ShipVia int, @Freight money, @ShipName nvarchar(40), @ShipAddress nvarchar(60), @ShipCity nvarchar(15), @ShipRegion nvarchar(15), @ShipPostalCode nvarchar(10), @ShipCountry nvarchar(15)AS UPDATE [Orders] SET [CustomerID] = @CustomerID, [EmployeeID] = @EmployeeID, [OrderDate] = @OrderDate, [RequiredDate] = @RequiredDate, [ShippedDate] = @ShippedDate, [ShipVia] = @ShipVia, [Freight] = @Freight, [ShipName] = @ShipName, [ShipAddress] = @ShipAddress, [ShipCity] = @ShipCity, [ShipRegion] = @ShipRegion, [ShipPostalCode] = @ShipPostalCode, [ShipCountry] = @ShipCountryWHERE [OrderID] = @OrderID 这是一个更新Northwind数据库Orders表的存储过程,因为是一个十分普通的存储过程,所以是一个很好的代码生成模型。模板的目标是从表里读取信息然后自动生成对应的存储过程,当然可以适用其他表不仅仅是本表。首先在模板里创建CodeTemplate标签描述模板名字和说明。<%@ CodeTemplate Language="C#" TargetLanguage="T-SQL" Description="Generates a update stored procedure." %>接着我们需要调用包含了SchemaExplorer的程序集,此类有利于我们通过程序集标签访问数据库。<%@ Assembly Name="SchemaExplorer" %> 我们需要使用import标签导入SchemaExplorer的命名空间,载入SchemaExplorer程序集,以便我们可以在模板里访问它的类,<%@ Import Namespace="SchemaExplorer" %>因为我们打算从表里读数据,我们需要在模板里增加一个TableSchema类型的属性。<%@ Property Name="SourceTable" Type="SchemaExplorer.TableSchema" Category="Context" Description="Table that the stored procedures should be based on." %> 当我们执行模板时,这个属性允许我们选择一个数据库和表格。我们可以参考这个属性去检测表和创建基于表的内容和属性的我们的模板。接着开始些输出文本的模板节点。第一个节点是如下可见的文件头。------------------------------------------------------------------- Date Created: <%= DateTime.Now.ToLongDateString() %>-- Created By: Generated by CodeSmith-----------------------------------------------------------------代码和最后例子里的类似,我们使用DateTime对象为我们的文件生成时间。然后我们需要创建存储过程脚本的首行:CREATE PROCEDURE dbo.Update<%= SourceTable.Name %>在本行里我们使用早先定义的 SourceTable的Name属性,它将插入我们在测试的表名,因此本例中因为我们对应的是Orders表所以存储过程将是UPdateOrders.接下来我们为存储过程创建参数列表,因为我们要创建一个更新的存储过程我们需要表的每个列名做参数。使用TableSchema 对象我们可以循环表中的列读出列名和列的数据类型,使用值去创建我们的列表。 <% for (int i = 0; i < SourceTable.Columns.Count; i++) { %> <%= GetSqlParameterStatement(SourceTable.Columns[i]) %> <% if (i < SourceTable.Columns.Count - 1) { %>,<% } %> <% } %>AS我们需要创建一个带参数的更新表的SQL语句。因此我需要再循环列集合,但是应该用NonPrimaryKeyColumns 集合替换normal columns 进行。我们这样做是因为我们仅想更行非主建列。我们将在更新语句的where条件中用主键列。UPDATE [<%= SourceTable.Name %>] SET <% for (int i = 0; i < SourceTable.NonPrimaryKeyColumns.Count; i++) { %> [<%= SourceTable.NonPrimaryKeyColumns[i].Name %>] = @<%= SourceTable.NonPrimaryKeyColumns[i].Name %><% if (i < SourceTable.NonPrimaryKeyColumns.Count - 1) { %>,<% } %> <% } %> 下列的语句中我们要创建SET列表,列表包含了列名和参数名,下面是CodeSmith执行后生成的文本:UPDATE [Orders] SET [CustomerID] = @CustomerID, [EmployeeID] = @EmployeeID, [OrderDate] = @OrderDate, [RequiredDate] = @RequiredDate, [ShippedDate] = @ShippedDate, [ShipVia] = @ShipVia, [Freight] = @Freight, [ShipName] = @ShipName, [ShipAddress] = @ShipAddress, [ShipCity] = @ShipCity, [ShipRegion] = @ShipRegion, [ShipPostalCode] = @ShipPostalCode, [ShipCountry] = @ShipCountry模板的最后部分我们需要编写update语句的where字句。再次的使用列循环,但是这次我们从表的主见列中循环来生成where字句。WHERE <% for (int i = 0; i < SourceTable.PrimaryKey.MemberColumns.Count; i++) { %> <% if (i > 0) { %>AND <% } %> [<%= SourceTable.PrimaryKey.MemberColumns[i].Name %>] = @ <%= SourceTable.PrimaryKey.MemberColumns[i].Name %> <% } %>下面是CodeSmith生成的文本:WHERE [OrderID] = @OrderID下面是完整的模板:<%@ CodeTemplate Language="C#" TargetLanguage="T-SQL" Description="Generates a update stored procedure." %> <%@ Property Name="SourceTable" Type="SchemaExplorer.TableSchema" Category="Context" Description="Table that the stored procedures should be based on." %> <%@ Assembly Name="SchemaExplorer" %> <%@ Import Namespace="SchemaExplorer" %> <script runat="template">public string GetSqlParameterStatement(ColumnSchema column){ string param = "@" + column.Name + " " + column.NativeType; switch (column.DataType) { case DbType.Decimal: { param += "(" + column.Precision + ", " + column.Scale + ")"; break; } default: { if (column.Size > 0) { param += "(" + column.Size + ")"; } break; } } return param;}</script> ------------------------------------------------------------------- Date Created: <%= DateTime.Now.ToLongDateString() %>-- Created By: Generated by CodeSmith----------------------------------------------------------------- CREATE PROCEDURE dbo.Update<%= SourceTable.Name %> <% for (int i = 0; i < SourceTable.Columns.Count; i++) { %> <%= GetSqlParameterStatement(SourceTable.Columns[i]) %><% if (i < SourceTable.Columns.Count - 1) { %>,<% } %> <% } %>AS UPDATE [<%= SourceTable.Name %>] SET <% for (int i = 0; i < SourceTable.NonPrimaryKeyColumns.Count; i++) { %> [<%= SourceTable.NonPrimaryKeyColumns[i].Name %>] = @<%= SourceTable.NonPrimaryKeyColumns[i].Name %><% if (i < SourceTable.NonPrimaryKeyColumns.Count - 1) { %>,<% } %> <% } %>WHERE <% for (int i = 0; i < SourceTable.PrimaryKey.MemberColumns.Count; i++) { %> <% if (i > 0) { %>AND <% } %> [<%= SourceTable.PrimaryKey.MemberColumns[i].Name %>] = @<%= SourceTable.PrimaryKey.MemberColumns[i].Name %> <% } %>当我们在CodeSmith中载入模板的时候可以看见 SourceTable 属性,属性在右边包含了一个按钮,你可以通过选择一个表来运行存储过程生成的模板。下面是界图: 点右边的按钮去选择一个表,你可以看见如下图: 在界图中你可以配置你的数据源(通过选择展开按钮选择另一 个数据库)。接着选你想要模板生成代码的表。选择表点生成 后下面的文本将被生成: ------------------------------------------------------------------- Date Created: Saturday, December 06, 2003-- Created By: Generated by CodeSmith----------------------------------------------------------------- CREATE PROCEDURE dbo.UpdateOrders @OrderID int, @CustomerID nchar(5), @EmployeeID int, @OrderDate datetime, @RequiredDate datetime, @ShippedDate datetime, @ShipVia int, @Freight money, @ShipName nvarchar(40), @ShipAddress nvarchar(60), @ShipCity nvarchar(15), @ShipRegion nvarchar(15), @ShipPostalCode nvarchar(10), @ShipCountry nvarchar(15)AS UPDATE [Orders] SET [CustomerID] = @CustomerID, [EmployeeID] = @EmployeeID, [OrderDate] = @OrderDate, [RequiredDate] = @RequiredDate, [ShippedDate] = @ShippedDate, [ShipVia] = @ShipVia, [Freight] = @Freight, [ShipName] = @ShipName, [ShipAddress] = @ShipAddress, [ShipCity] = @ShipCity, [ShipRegion] = @ShipRegion, [ShipPostalCode] = @ShipPostalCode, [ShipCountry] = @ShipCountryWHERE [OrderID] = @OrderID本模板可以生成任意数据库任意表的更新存储过程。结论真心的希望这个快速帮助可以为你使用CodeSmith提供足够的信息和例子.CodeSmith是一个十分有力和省时的工具,希望你可以在你的工程中发现更多的用处就像我们一样。
