首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

【T4实践(三)】如果更快更好的编写T4模板

Posted on 2012-02-20 16:03  停留的风  阅读(5674)  评论(5编辑  收藏  举报

如何快速高效的写出高质量的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; }

}
}

四、源码地址

源码地址,点击下载


如果觉的对您有帮助,请推荐!