如何快速高效的寫出高質量的T4模板呢?
一、總結:先驗證C#代碼,然后轉T4模板!
通過實踐,總結如下:先驗證C#代碼,然后轉T4模板!
因為T4模板難以調試,以后會就調試,專門拿一張來介紹。T4模板編寫的實質就是腳本代碼和文本。腳本代碼通常就是C#和VB.Net。主要邏輯都在腳本代碼中。而C#代碼調試要簡單方便的多。所以,在編寫T4模板的時候,我們完全,可以先寫C#代碼,然后再修改為T4模板。需要修改的地方,只是將C#放在<##>標記中,一些引用需要修改,而模板還有個好處,就是不用定義類型,直接添加默認函數或者屬性
本文將結合實際例子,來進行說明。
二、實例:代碼生成器
該實例,就是我們通常見過的,模型代碼生成器,根據數據模型生成代碼框架,如模型層、數據訪問層等。
本實例先根據數據表,生存模型層代碼。這是一個簡單實例,如果有功夫,您可以根據這個原理寫出自己的代碼生成框架。
三、具體步驟:
1、預置數據
--創建庫
CREATE DATABASE TestDB
--創建表
CREATE TABLE PersonInfo
(
ID INT PRIMARY KEY NOT NULL,
NAME nvarchar(50) NOT NULL,
Company NVARCHAR(50) NULL,
Job NVARCHAR(20) NULL
)
2、寫C#代碼,並驗證通過。可用Console程序驗證
模型管理器:主要實現,數據訪問、獲取表結構
using System;
using System.Data.SqlClient;
using System.Data;
namespace T4_3FiledGenerator
{
public class ModelManager
{
/// <summary>
/// 數據庫連接字符串
/// </summary>
private const string CONNECTION_STRING= "server=localhost;database=testDB;uid=sa;pwd=ufsoft;";
/// <summary>
/// 用戶信息表名
/// </summary>
private const string PERSONINFO_TABLE_NAME = "PersonInfo";
/// <summary>
/// 根據表名查詢表結構信息
/// </summary>
private const string SELECT_SCHEMA_BY_TABLE_NAME = @"SELECT TABLE_NAME AS TableName ,
ORDINAL_POSITION AS ColumnID ,
COLUMN_NAME AS ColumnName ,
DATA_TYPE AS DataType ,
CASE WHEN IS_NULLABLE = 'NO' THEN 'false'
ELSE 'true'
END AS IsNullable
FROM INFORMATION_SCHEMA.COLUMNS AS A
LEFT JOIN sysobjects AS B ON A.TABLE_NAME = B.name
WHERE A.TABLE_NAME = '{0}'";
/// <summary>
/// 獲得數據連接
/// </summary>
/// <returns></returns>
private SqlConnection GetConnection()
{
return new SqlConnection(CONNECTION_STRING);
}
/// <summary>
/// 釋放連接
/// </summary>
/// <param name="con"></param>
private void ReleaseConnection(SqlConnection con)
{
if (con != null)
{
if (con.State == ConnectionState.Open)
{
con.Close();
}
}
}
/// <summary>
///
/// </summary>
/// <param name="tableName"></param>
public DataTable GetTableSchema(string tableName)
{
DataTable dt;
using (SqlConnection con = GetConnection())
{
con.Open();
SqlCommand cmd = con.CreateCommand();
cmd.CommandText = string.Format(SELECT_SCHEMA_BY_TABLE_NAME,tableName);
cmd.CommandType = CommandType.Text;
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
adapter.Fill(ds);
dt = ds.Tables[0];
}
return dt;
}
/// <summary>
///
/// </summary>
public void Generate()
{
DataTable table = GetTableSchema(PERSONINFO_TABLE_NAME);
if (table != null && table.Rows.Count > 0)
{
foreach (DataRow row in table.Rows)
{
Console.WriteLine("public class {0}", row["TableName"]);
Console.WriteLine("public {0} {1}", TransFromSqlType(row["DataType"].ToString()), row["ColumnName"]);
}
}
}
/// <summary>
/// SQL
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public string TransFromSqlType(string type)
{
if (string.IsNullOrEmpty(type))
{
return string.Empty;
}
if (string.Equals(type, "int", StringComparison.OrdinalIgnoreCase))
{
return "int";
}
else if (string.Equals(type, "nvarchar", StringComparison.OrdinalIgnoreCase))
{
return "string";
}
return "string";
}
}
}
測試程序:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace T4_3FiledGenerator
{
class Program
{
static void Main(string[] args)
{
ModelManager manager = new ModelManager();
manager.Generate();
}
}
}
3、C#代碼轉換成T4
3.1 添加引用,在C#中一般都是直接用using,在這里,需要改為import方式,如:<#@ import namespace="System.Data" #>
如果有些dll,需要添加引用,則需要assembly,如:<#@ assembly name="System.Data" #>。
對於程序集的加載,通常有兩種情況,
(1)如果C#自帶的,則直接用dll的名稱即可,
(2)如果實自定義DLL文件,需要添加全路徑,這是最簡單的方法,還有其他一些方法,這里暫且不討論。
3.2 將寫成的C#代碼,放在<#+ #>標簽中,一般放置在T4模板最后
3.3 調用C#的方法,初始化變量
3.4 編寫T4文本模板
3.5 對模板中的一些變量,調用2.3初始化的變量進行賦值
具體的T4代碼如下:
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Data" #>
<#@ assembly name="System.Xml" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Data" #>
<#@ import namespace="System.Data.SqlClient" #>
<#
ModelManager manager = new ModelManager();
string tableName = "PersonInfo";
DataTable table= manager.GetTableSchema(tableName);
#>
using System;
namespace Model
{
public class <#= tableName #>
{
<#
foreach(DataRow row in table.Rows)
{
#>
public <#= manager.TransFromSqlType(row["DataType"].ToString())#> <#=row["ColumnName"]#>{ get; set; }
<#}
#>
}
}
<#+
public class ModelManager
{
/// <summary>
/// 數據庫連接字符串
/// </summary>
private const string CONNECTION_STRING= "server=localhost;database=testDB;uid=sa;pwd=ufsoft;";
/// <summary>
/// 用戶信息表名
/// </summary>
private const string PERSONINFO_TABLE_NAME = "PersonInfo";
/// <summary>
/// 根據表名查詢表結構信息
/// </summary>
private const string SELECT_SCHEMA_BY_TABLE_NAME = @"SELECT TABLE_NAME AS TableName ,
ORDINAL_POSITION AS ColumnID ,
COLUMN_NAME AS ColumnName ,
DATA_TYPE AS DataType ,
CASE WHEN IS_NULLABLE = 'NO' THEN 'false'
ELSE 'true'
END AS IsNullable
FROM INFORMATION_SCHEMA.COLUMNS AS A
LEFT JOIN sysobjects AS B ON A.TABLE_NAME = B.name
WHERE A.TABLE_NAME = '{0}'";
/// <summary>
/// 獲得數據連接
/// </summary>
/// <returns></returns>
private SqlConnection GetConnection()
{
return new SqlConnection(CONNECTION_STRING);
}
/// <summary>
/// 釋放連接
/// </summary>
/// <param name="con"></param>
private void ReleaseConnection(SqlConnection con)
{
if (con != null)
{
if (con.State == ConnectionState.Open)
{
con.Close();
}
}
}
/// <summary>
///
/// </summary>
/// <param name="tableName"></param>
public DataTable GetTableSchema(string tableName)
{
DataTable dt;
using (SqlConnection con = GetConnection())
{
con.Open();
SqlCommand cmd = con.CreateCommand();
cmd.CommandText = string.Format(SELECT_SCHEMA_BY_TABLE_NAME,tableName);
cmd.CommandType = CommandType.Text;
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
adapter.Fill(ds);
dt = ds.Tables[0];
}
return dt;
}
/// <summary>
///
/// </summary>
public void Generate()
{
DataTable table = GetTableSchema(PERSONINFO_TABLE_NAME);
if (table != null && table.Rows.Count > 0)
{
foreach (DataRow row in table.Rows)
{
Console.WriteLine("public class {0}", row["TableName"]);
Console.WriteLine("public {0} {1}", TransFromSqlType(row["DataType"].ToString()), row["ColumnName"]);
}
}
}
/// <summary>
/// SQL
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public string TransFromSqlType(string type)
{
if (string.IsNullOrEmpty(type))
{
return string.Empty;
}
if (string.Equals(type, "int", StringComparison.OrdinalIgnoreCase))
{
return "int";
}
else if (string.Equals(type, "nvarchar", StringComparison.OrdinalIgnoreCase))
{
return "string";
}
return "string";
}
}
#>
4、生成代碼並調試修改。
其實轉過來時,出現錯誤的幾率已經很少,比較難調的就是某些組件的引用。
5、結果,T4模板生成的代碼
using System;
namespace Model
{
public class PersonInfo
{
public int ID { get; set; }
public string NAME { get; set; }
public string Company { get; set; }
public string Job { get; set; }
}
}
四、源碼地址
如果覺的對您有幫助,請推薦!