如何快速高效的写出高质量的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; }
}
}
四、源码地址
如果觉的对您有帮助,请推荐!