一、一个简单的例子
这个例子仅是一个简单的应用,在我翻译并学习完CodeSmith的英文帮助文档后,对CodeSmith有了一定的了解,开始着手编写一些CodeSmith应用模板,今天按照最早提到的例子自行编写了一个基于表的添加存储过程的生成模板。具体语法前面基础中已做过详细解释这里仅是一个小综合应用的例子,望对大家学习CodeSmith有很好的帮助。我的同事也写了几个CodeSmith的技巧的文章http://terrylee.cnblogs.com/大家有空去看看,写的很不错哦,都是针对于CodeSmith自定义属性编写的东东:)
1<%@ CodeTemplate Language="C#" TargetLanguage="T-SQL" Description="Create a procedure which have insert function base on a table." %>
2<%@ Assembly Name="SchemaExplorer" %>
3<%@ Imp
4<%@ Property Name="SourceTable" Type="SchemaExplorer.TableSchema" Category="DataTable" Description="Table that the stored procedures should be based on." %>
5<%@ Property Name="Author" Type="String" Category="Context" Description="The author for this procedure."%>
6<%@ Property Name="Description" Type="String" Category="Context" Description="The description for this procedure."%>
7<script runat="template">
8public string GetSqlParameterStatement(ColumnSchema column)
9{
10 string param = "@" + column.Name + " " + column.NativeType;
11 switch (column.DataType)
12 {
13 case DbType.Decimal:
14 {
15 param += "(" + column.Precision + ", " + column.Scale + ")";
16 break;
17 }
18 default:
19 {
20 if (column.Size > 0)
21 {
22 param += "(" + column.Size + ")";
23 }
24 break;
25 }
26 }
27 return param;
28}
29</script>
30CREATE PROCEDURE dbo.<%=SourceTable.Name %>Insert
31/*
32==================================================
33Author:<%= Author %>
34CreatedTime:<%= System.DateTime.Now.ToShortDateString() %>
35Description:<%= Description %>
36==================================================
37*/
38<% for (int i = 0; i < SourceTable.Columns.Count; i++) { %>
39<%= GetSqlParameterStatement(SourceTable.Columns[i]) %><% if (i < SourceTable.Columns.Count - 1) { %>,<% } %> <% if (SourceTable.Columns[i].Description != "") { %>--<%= SourceTable.Columns[i].Description %><% } %>
40<% } %>
41AS
42Insert Into [<%= SourceTable.Name %>]
43(
44<% for (int i = 0; i < SourceTable.Columns.Count; i++) { %>
45[<%= SourceTable.Columns[i].Name %>]<% if (i < SourceTable.Columns.Count - 1) { %>,<% } %> <% if (SourceTable.Columns[i].Description != "") { %>--<%= SourceTable.Columns[i].Description %><% } %>
46<% } %>
47)
48Values
49(
50<% for (int i = 0; i < SourceTable.Columns.Count; i++) { %>
51@<%= SourceTable.Columns[i].Name %><% if (i < SourceTable.Columns.Count - 1) { %>,<% } %>
52<% } %>
53)
二、具有删除功能的模板
今天又根据CodeSmith的几个基本组件写出了基于表生成删除功能的存储过程代码生成模板。
昨天觉得添加的存储过程模板写的比较简单,今天准备详细介绍一下这个删除的模板。
首先介绍我们使用到的一个教本函数GetSqlParameterStatement(ColumnSchema column),其函数代码如下:
1public string GetSqlParameterStatement(ColumnSchema column)
2{
3 string param = "@" + column.Name + " " + column.NativeType;
4 switch (column.DataType)
5 {
6 case DbType.Decimal:
7 {
8 param += "(" + column.Precision + ", " + column.Scale + ")";
9 break;
10 }
11 default:
12 {
13 if (column.Size > 0)
14 {
15 param += "(" + column.Size + ")";
16 }
17 break;
18 }
19 }
20 return param;
21}
大家可以看到,这个函数需要传入一个ColumnSchema类型的参数,它代表一个数据表中的列,并且是一个列,然后根据ColumnSchema这个类具有的属性,对传入的列进行一些操作然后返回我们生成存储过程时需要的代码。
首先介绍一下ColumnSchema的一些常用属性,如下表: 属性Property
描述Description
AllowDBNull
是否允许空值NULL
Database
通过DatabaseSchema对象得到当前列所属的数据库
DataType
此数据对象的数据类型
Description
当前对象的描述
ExtendedProperties
用来存储SchemaObject的其他附加信息
IsForeignKeyMember
当前列是否为外键
IsPrimaryKeyMember
当前列是否为主键
IsUnique
当前列是否唯一
Name
列的名称
NativeType
列定义的数据类型
Precision
数据对象的精度
Scale
数据对象的范围(个人理解为需要保留小数的范围)
Size
数据对象的大小(例如:字符串长度为10)
SystemType
数据对象的系统类型
Table
当前列所属的数据表
下面为我们首先要生成存储过程,要自动生成的代码分成了红、绿、蓝三部分。
CREATE PROCEDURE dbo.CustomersDelete
/*
==================================================
Author:Bear-Study-Hard
CreatedTime:2005-12-28
Description:Delete a record from table Customers
==================================================
*/
@CustomerID nchar(5) --客户ID
AS
Delete From [Customers]
Where
[CustomerID] = @CustomerID
我们的这个脚本函数就是要实现拼出红色的部分,GetSqlParameterStatement函数接收到ColumnSchema类型的参数后,从其Name属性和NativeType属性拼出@CustomerID nchar部分,然后由于不同的数据类型尤其是数值类型和字符串类型的区别,会导致数据类型的大小会有所不同,这里仅对Decimal的数据类型进行了判断(Numeric和float等均需要这种处理),然后根据Precision属性得到精度并通过Scale属性得到了需要保留小数的范围。如果传出的为非Decimal类型字段则直接通过Size属性取出其大小即可。得到了(5)部分。最后的注释是为了生成的存储过程解读性好加上的,使用的是Description属性。
剩下的绿色部分和蓝色部分生成时比较简单,请各位自行学习。模板代码为:
1<%@ CodeTemplate Language="C#" TargetLanguage="T-SQL" Description="Create a procedure which have delete function base on a table.Must use PrimaryKey to delete a record." %>
2<%@ Assembly Name="SchemaExplorer" %>
3<%@ Imp
4<%@ Property Name="SourceTable" Type="SchemaExplorer.TableSchema" Category="DataTable" Description="Table that the stored procedures should be based on." %>
5<%@ Property Name="Author" Type="String" Category="Context" Description="The author for this procedure." Optional="true"%>
6<%@ Property Name="Description" Type="String" Category="Context" Description="The description for this procedure." Optional="true"%>
7<script runat="template">
8public string GetSqlParameterStatement(ColumnSchema column)
9{
10 string param = "@" + column.Name + " " + column.NativeType;
11 switch (column.DataType)
12 {
13 case DbType.Decimal:
14 {
15 param += "(" + column.Precision + ", " + column.Scale + ")";
16 break;
17 }
18 default:
19 {
20 if (column.Size > 0)
21 {
22 param += "(" + column.Size + ")";
23 }
24 break;
25 }
26 }
27 return param;
28}
29</script>
30CREATE PROCEDURE dbo.<%=SourceTable.Name %>Delete
31/*
32==================================================
33Author:<%= Author %>
34CreatedTime:<%= System.DateTime.Now.ToShortDateString() %>
35Description:<%= Description %>
36==================================================
37*/
38<% for (int i = 0; i < SourceTable.PrimaryKey.MemberColumns.Count; i++) { %>
39<%= GetSqlParameterStatement(SourceTable.PrimaryKey.MemberColumns[i]) %><% if (i < SourceTable.PrimaryKey.MemberColumns.Count - 1) { %>,<% } %> <% if (SourceTable.Columns[i].Description != "") { %>--<%= SourceTable.Columns[i].Description %><% } %>
40<% } %>
41AS
42Delete From [<%= SourceTable.Name %>]
43Where
44<% for (int i = 0; i < SourceTable.PrimaryKey.MemberColumns.Count; i++) { %>
45<% if (i > 0) { %>AND <% } %>[<%= SourceTable.PrimaryKey.MemberColumns[i].Name %>] = @<%= SourceTable.PrimaryKey.MemberColumns[i].Name %>
46<% } %>
三、微软的一个例子
今天在微软的网站看到的一篇使用CodeSmith的例子,现在写出来大家一起研究研究。
首先,我还是要简要介绍一下其中用到的基础知识。
1.在模板中的代码区中(<%= %>或<% %>)可以使用.NET中的一些类和方法。但是就像和.NET项目中一样需要添加应用,就像C#中的using
<%@ Assembly Name="System.Da
<%@ Imp
2.在脚本区域中可以编写生成模板时使用到的函数,其中的语言根据在声明模板时定义的使用的语言相同。
<script runat="template">
</script>
3.个人感觉在编写模板时就像以前编写ASP页面一样,很灵活。其中的循环和判断的使用结构上和ASP基本一样,只不过在代码区域中使用的语言是声明模板时定义的语言,像C#、VB.NET等。
这次我们要实现的一个功能是将某一文件夹下所有匹配后缀的文件全部找出并输出它们的绝对路径。其实这个模板本身的功能并不强大,主要是大家可以利用这个模板在生成代码时批量向代码文件中生成代码,例如向某一文件夹下的所有.cs文件中插入相应的代码。这个思想是基于一定条件的:①首先要学习我前边在CodeSmith基础中提到的,可以向代码文件中定义的region中添加相应的代码,②文件夹下的.cs文件需要有一定的命名规范,③.cs代码文件中的region区域的定义的名称也需要有一定的命名规范,②③这两个要求是结合起来的,因为我们得到同一文件夹下的所有.cs文件后,根据文件名去确定这个代码文件是哪一层的那个类文件,通过这个名称我们就知道需要读出那个数据表的结构,然后按照这个数据表去像相应命名好的region中添加相应的代码。主要思想就是这样的。下面将模板文件的代码贴出来大家研究。
1<%@ CodeTemplate Language="C#" TargetLanguage="Text" Description="Simple template to show main syntax" %>
2<%@ Property Name="Filter" Default="*.cst" Type="System.string" Category="Masks" Description="Mask for files in the directory" %>
3<%@ Assembly Name="SchemaExplorer" %>
4<%@ Assembly Name="System.Design" %>
5<%@ Imp
6<%@ Imp
7Simple Template Example used to show syntax and structure of template.
8
9<%= DateTime.Now.ToLongDateString() %>
10
11<%
12// Comments within co
13// http://www.livebaby.cn ( C#)
14Response.WriteLine("List of files in template directory (using mask "+ Filter + ")");
15DisplayDirectoryContents(Filter);
16Response.WriteLine(">> Co
17%>
18
19<script runat="template">
20// Iterates through the current directory and displays
21// a list of the files that conform to the supplied
22// mask.
23public void DisplayDirectoryContents(string sFilter)
24{
25 string[] dirFiles = Directory.GetFiles
26 (this.CodeTemplateInfo.DirectoryName, sFilter);
27 for (int i = 0; i < dirFiles.Length; i++)
28 {
29 Response.WriteLine(dirFiles[i]);
30 }
31}
32</script>
四、实现选择路径对话框
首先我们要添加<%@ Assembly Name="System.Design" %>命名空间。然后我们在模板中自定义一个属性,用来表示要存储的路径。其中我们使用了this.CodeTemplateInfo.DirectoryName得到当前模版所在路径作为默认路径。
private string _outputDirectory = String.Empty;
[Editor(typeof(System.Windows.Forms.Design.FolderNameEditor), typeof(System.Drawing.Design.UITypeEditor))]
[Optional]
[Category("Output")]
[Description("The directory to output the results to.")]
public string OutputDirectory
{
get
{
// default to the directory that the template is located in
if (_outputDirectory.Length == 0) return this.CodeTemplateInfo.DirectoryName + "output\\";
return _outputDirectory;
}
set
{
if (!value.EndsWith("\\")) value += "\\";
_outputDirectory = value;
}
}
这样编译运行后我们就可以看到如下效果:
单击选择路径按钮后我们就可以看到这样的窗口
选择后相应的路径值就会填入属性框。