一、一個簡單的例子
這個例子僅是一個簡單的應用,在我翻譯並學習完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<%@ Import Namespace="SchemaExplorer" %>
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<%@ Import Namespace="SchemaExplorer" %>
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.Data" %>
<%@ Import Namespace="System. Data" %>
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<%@ Import Namespace="SchemaExplorer" %>
6<%@ Import Namespace="System.IO" %>
7Simple Template Example used to show syntax and structure of template.
8
9<%= DateTime.Now.ToLongDateString() %>
10
11<%
12// Comments within code delimiters or script blocks
13// http://www.livebaby.cn ( C#)
14Response.WriteLine("List of files in template directory (using mask "+ Filter + ")");
15DisplayDirectoryContents(Filter);
16Response.WriteLine(">> Code Generation Complete.");
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;
}
}
這樣編譯運行後我們就可以看到如下效果:
單擊選擇路徑按鈕後我們就可以看到這樣的窗口
選擇後相應的路徑值就會填入屬性框。