[.NET]透過 T4 產生對應 DB table 的 entity

[.NET]透過 T4 產生對應 DB table 的 entity

前言

在前面的 RowMapper 文章裡,提到透過自訂 Attribute 來封裝 entity 與 DB 資料欄位的對應,以企圖讓 data access layer 回來的資料,都是以 entity 物件的形式,而非 DataTable 或 DataSet 這類弱型別的結構。

但,難道每次設計 mapping table 的 entity 都只能手動去刻嗎?感覺好蠢、好浪費時間。以 LINQ to SQL 或 LINQ to EF,都只需要拉一拉 data model,就可以自動產生對應的 entity 集合。刻 entity 實在太浪費時間了。

沒錯,所以有很多人會自己寫對應的程式碼產生器,甚至許多套件也都有提供類似的功能。這一篇文要介紹的,是透過內建的 T4 轉換,用最簡單的方式來自動產生我們所需要的 entity class 檔案。

註:有些人可能會說,為什麼不乾脆使用 LINQ to SQL 或 LINQ to EF,但實際上很多現實狀況,是不允許使用的。不管是 .net framework migration 的問題,或是 performance 問題,或是需要在DA層自訂一些客製化的動作時,就不一定可以直接使用。

 

簡介

T4 的全名是「Text Template Transformation Toolkit」,簡單的說,就是一個可以轉換的文字範本,在範本裡面可以透過程式碼與邏輯,來決定要嵌入哪些文字。幾乎現在的 Visual Studio 上 的 code generator 底子都是透過 T4 來產生對應的程式碼。

 

範例

需求說明:

  1. 針對某一個DB中,某一張 table
  2. 產生對應的 class 內容
  3. Class 名稱預設與 table 名稱相同
  4. Property 名稱預設與 table column 名稱相同
  5. Property type 預設為 column type 所對應的 data type
  6. 每一個 property 上,要有對應的自訂 attribute,供 RowMapper 使用
  7. 可自行加入 todo 註解,或是相關 using namespace

在任何一個專案底下,新增一個項目,類型為「文字範本」,請見下圖:

image

接著會出現下面的視窗,其實就是 T4 要幫忙轉換輸出結果的動作,當按下確定,就會按照 tt 上的範本邏輯產生對應的程式碼。第一次所產生的程式碼其實是空的,所以按確定跟取消都沒差。

image

接下來按照我們的需求,來設計 template 的內容:

<#@ template language="C#" debug="True" hostspecific="True" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Data" #>
<#@ assembly name="System.xml" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Data.SqlClient" #>
<#@ import namespace="System.Data" #>

using System;

//todo: 請修改對應的namespace
namespace MyProject.Entities 
{        
		<#
			//修改connection string
			string connectionString = "你的ConnectionString"; 
			SqlConnection conn = new SqlConnection(connectionString); 
			conn.Open(); 
			
			//如果需要database中全部table,則使用conn.GetSchema("Tables")即可
			string[] restrictions = new string[4];
			restrictions[1] = "dbo";
			//修改table名稱
			restrictions[2] = "你的Table名稱";
			DataTable schema = conn.GetSchema("Tables", restrictions);
			
			string selectQuery = "select * from  @tableName"; 
			SqlCommand command = new SqlCommand(selectQuery,conn); 
			SqlDataAdapter ad = new SqlDataAdapter(command); 
			System.Data.DataSet ds = new DataSet(); 
			
			foreach(System.Data.DataRow row in schema.Rows) 
			{ 				
			#>             
			//mapping table name: <#= row["TABLE_NAME"].ToString().Trim('s') #>
			public class <#= row["TABLE_NAME"].ToString().Trim('s') #>                            
			{
				<#                 
					command.CommandText = selectQuery.Replace("@tableName",row["TABLE_NAME"].ToString()); 
					ad.FillSchema(ds, SchemaType.Mapped, row["TABLE_NAME"].ToString());

					foreach (DataColumn dc in ds.Tables[0].Columns)
					{
						#>
[ColumnMappingAttribute("<#= dc.ColumnName #>")]                        
				public <#= dc.DataType.Name #> <#= dc.ColumnName #>  { get; set; }                                        
					
				<#    }                 #>                                
			}                            
			<#    
			}  #>
}

簡單的說,有點像 XSLT 轉換,或是 .aspx 的資料繫結,又或是 .asp 的寫法:

  1. <# #>區塊裡面的就是實際執行的程式碼
  2. 不在<# #>區塊的部分,則是文字範本
  3. 透過兩者的組合,就可以達到 code generator 的效果。

上面的範本主要做的事情如下:

  1. 透過 connection string,建立與 DB 的連線
  2. 透過指定的 table name,來針對我們需要的 table 產生對應的 entity
  3. 透過 GetSchema() 取得所有 Tables 的結構
  4. 針對所有 table (若有給定 table name,則只針對該 table),產生所有對應的 class , class的名稱為 TABLE_NAME 欄位的值。
  5. 針對 table 上每一個欄位,產生對應的 public property,這邊是用 C# 3.0 的自動實作屬性,所以只產生 {get; set;}
  6. 每一個 property 上,都要加上自訂的 attribute: ColumnMappingAttribute,對應的 column name,則以該 ColumnName 值為準。

範本建立完成後,只要存檔,跳出上面的視窗後,按下確定,就會產生對應的 cs 檔出來 (副檔名是由範本第二行的 output extension=".cs" 決定的)

結果範例如下:

image

只需要加入對應的 namespace ,並格式化一下,就是一個 mapping Log table 的 entity 了。

另外值得一提的是,T4 的 template 建立完以後,在任何一個 project 上想使用時,只需要直接把該檔案拉進 Visual Studio 中就可以了。

 

結論

T4 其實用處還有很多,這篇只是小小的應用一下,用來解決自己日常作業上常見的需求。

透過這一篇,希望大家可以延伸出一些屬於自己的小應用,可以提昇自己的生產力。

 

Reference

  1. T4: Text Template Transformation Toolkit
  2. Code Generation Using T4 Templates

 

Sample Code

download: T4 sample.zip

想收到第一手公開培訓課程資訊,或想詢問企業內訓、顧問、教練、諮詢服務的,請加 Odd-e Line 帳號