1. 程式人生 > >使用T4模板從根據資料庫自動生成類類檔案

使用T4模板從根據資料庫自動生成類類檔案

一個數據庫可能有多個表,需要生成多個類檔案,首先建立一個xx.ttinclude檔案.此檔案主要能夠生成單獨的類檔案。同時再定義一個類DBHelper來獲取資料庫裡所有表的資訊和資料。

<#@ assembly name="System.Core"#>
<#@ assembly name="EnvDTE"#>
<#@ import namespace="System.Collections.Generic"#>
<#@ import namespace="System.IO"#>
<#@ import namespace="System.Text"#>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating"#>

<#+

class Manager
{
public struct Block {
public String Name;
public int Start, Length;
}

public List<Block> blocks = new List<Block>();
public Block currentBlock;
public Block footerBlock = new Block();
public Block headerBlock = new Block();
public ITextTemplatingEngineHost host;
public ManagementStrategy strategy;
public StringBuilder template;
public String OutputPath { get; set; }

public Manager(ITextTemplatingEngineHost host, StringBuilder template, bool commonHeader) {
this.host = host;
this.template = template;
OutputPath = String.Empty;
strategy = ManagementStrategy.Create(host);
}

public void StartBlock(String name) {
currentBlock = new Block { Name = name, Start = template.Length };
}

public void StartFooter() {
footerBlock.Start = template.Length;
}

public void EndFooter() {
footerBlock.Length = template.Length - footerBlock.Start;
}

public void StartHeader() {
headerBlock.Start = template.Length;
}

public void EndHeader() {
headerBlock.Length = template.Length - headerBlock.Start;
}

public void EndBlock() {
currentBlock.Length = template.Length - currentBlock.Start;
blocks.Add(currentBlock);
}

public void Process(bool split) {
String header = template.ToString(headerBlock.Start, headerBlock.Length);
String footer = template.ToString(footerBlock.Start, footerBlock.Length);
blocks.Reverse();
foreach(Block block in blocks) {
String fileName = Path.Combine(OutputPath, block.Name);
if (split) {
String content = header + template.ToString(block.Start, block.Length) + footer;
strategy.CreateFile(fileName, content);
template.Remove(block.Start, block.Length);
} else {
strategy.DeleteFile(fileName);
}
}
}
}

class ManagementStrategy
{
internal static ManagementStrategy Create(ITextTemplatingEngineHost host) {
return (host is IServiceProvider) ? new VSManagementStrategy(host) : new ManagementStrategy(host);
}

internal ManagementStrategy(ITextTemplatingEngineHost host) { }

internal virtual void CreateFile(String fileName, String content) {
File.WriteAllText(fileName, content);
}

internal virtual void DeleteFile(String fileName) {
if (File.Exists(fileName))
File.Delete(fileName);
}
}

class VSManagementStrategy : ManagementStrategy
{
private EnvDTE.ProjectItem templateProjectItem;

internal VSManagementStrategy(ITextTemplatingEngineHost host) : base(host) {
IServiceProvider hostServiceProvider = (IServiceProvider)host;
if (hostServiceProvider == null)
throw new ArgumentNullException("Could not obtain hostServiceProvider");

EnvDTE.DTE dte = (EnvDTE.DTE)hostServiceProvider.GetService(typeof(EnvDTE.DTE));
if (dte == null)
throw new ArgumentNullException("Could not obtain DTE from host");

templateProjectItem = dte.Solution.FindProjectItem(host.TemplateFile);
}

internal override void CreateFile(String fileName, String content) {
base.CreateFile(fileName, content);
((EventHandler)delegate { templateProjectItem.ProjectItems.AddFromFile(fileName); }).BeginInvoke(null, null, null, null);
}

internal override void DeleteFile(String fileName) {
((EventHandler)delegate { FindAndDeleteFile(fileName); }).BeginInvoke(null, null, null, null);
}

private void FindAndDeleteFile(String fileName) {
foreach(EnvDTE.ProjectItem projectItem in templateProjectItem.ProjectItems) {
if (projectItem.get_FileNames(0) == fileName) {
projectItem.Delete();
return;
}
}
}
}#>

 

<#+
public class DBHelper
{
#region GetDbTables

public static List<DbTable> GetDbTables(string connectionString, string database, string tables = null)
{

if (!string.IsNullOrEmpty(tables))
{
tables = string.Format(" and obj.name in ('{0}')", tables.Replace(",", "','"));
}
#region SQL
string sql = string.Format(@"SELECT
obj.name tablename,
schem.name schemname,
idx.rows,
CAST
(
CASE
WHEN (SELECT COUNT(1) FROM sys.indexes WHERE object_id= obj.OBJECT_ID AND is_primary_key=1) >=1 THEN 1
ELSE 0
END
AS BIT) HasPrimaryKey
from {0}.sys.objects obj
inner join {0}.dbo.sysindexes idx on obj.object_id=idx.id and idx.indid<=1
INNER JOIN {0}.sys.schemas schem ON obj.schema_id=schem.schema_id
where type='U' {1}
order by obj.name", database, tables);
#endregion
DataTable dt = GetDataTable(connectionString, sql);
return dt.Rows.Cast<DataRow>().Select(row => new DbTable
{
TableName = row.Field<string>("tablename"),
SchemaName = row.Field<string>("schemname"),
Rows = row.Field<int>("rows"),
HasPrimaryKey = row.Field<bool>("HasPrimaryKey")
}).ToList();
}
#endregion

#region GetDbColumns

public static List<DbColumn> GetDbColumns(string connectionString, string database, string tableName, string schema = "dbo")
{
#region SQL
string sql = string.Format(@"
WITH indexCTE AS
(
SELECT
ic.column_id,
ic.index_column_id,
ic.object_id
FROM {0}.sys.indexes idx
INNER JOIN {0}.sys.index_columns ic ON idx.index_id = ic.index_id AND idx.object_id = ic.object_id
WHERE idx.object_id =OBJECT_ID(@tableName) AND idx.is_primary_key=1
)
select
colm.column_id ColumnID,
CAST(CASE WHEN indexCTE.column_id IS NULL THEN 0 ELSE 1 END AS BIT) IsPrimaryKey,
colm.name ColumnName,
systype.name ColumnType,
colm.is_identity IsIdentity,
colm.is_nullable IsNullable,
cast(colm.max_length as int) ByteLength,
(
case
when systype.name='nvarchar' and colm.max_length>0 then colm.max_length/2
when systype.name='nchar' and colm.max_length>0 then colm.max_length/2
when systype.name='ntext' and colm.max_length>0 then colm.max_length/2
else colm.max_length
end
) CharLength,
cast(colm.precision as int) Precision,
cast(colm.scale as int) Scale,
prop.value Remark
from {0}.sys.columns colm
inner join {0}.sys.types systype on colm.system_type_id=systype.system_type_id and colm.user_type_id=systype.user_type_id
left join {0}.sys.extended_properties prop on colm.object_id=prop.major_id and colm.column_id=prop.minor_id
LEFT JOIN indexCTE ON colm.column_id=indexCTE.column_id AND colm.object_id=indexCTE.object_id
where colm.object_id=OBJECT_ID(@tableName)
order by colm.column_id", database);
#endregion
SqlParameter param = new SqlParameter("@tableName", SqlDbType.NVarChar, 100) { Value = string.Format("{0}.{1}.{2}", database, schema, tableName) };
DataTable dt = GetDataTable(connectionString, sql, param);
return dt.Rows.Cast<DataRow>().Select(row => new DbColumn()
{
ColumnID = row.Field<int>("ColumnID"),
IsPrimaryKey = row.Field<bool>("IsPrimaryKey"),
ColumnName = row.Field<string>("ColumnName"),
ColumnType = row.Field<string>("ColumnType"),
IsIdentity = row.Field<bool>("IsIdentity"),
IsNullable = row.Field<bool>("IsNullable"),
ByteLength = row.Field<int>("ByteLength"),
CharLength = row.Field<int>("CharLength"),
Scale = row.Field<int>("Scale"),
Remark = row["Remark"].ToString()
}).ToList();
}

#endregion


#region GetDataTable

public static DataTable GetDataTable(string connectionString, string commandText, params SqlParameter[] parms)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand command = connection.CreateCommand();
command.CommandText = commandText;
command.Parameters.AddRange(parms);
SqlDataAdapter adapter = new SqlDataAdapter(command);

DataTable dt = new DataTable();
adapter.Fill(dt);

return dt;
}
}

#endregion
}

#region DbTable
/// <summary>
/// 表結構
/// </summary>
public sealed class DbTable
{
/// <summary>
/// 表名稱
/// </summary>
public string TableName { get; set; }
/// <summary>
/// 表的架構
/// </summary>
public string SchemaName { get; set; }
/// <summary>
/// 表的記錄數
/// </summary>
public int Rows { get; set; }

/// <summary>
/// 是否含有主鍵
/// </summary>
public bool HasPrimaryKey { get; set; }
}
#endregion

#region DbColumn
/// <summary>
/// 表字段結構
/// </summary>
public sealed class DbColumn
{
/// <summary>
/// 欄位ID
/// </summary>
public int ColumnID { get; set; }

/// <summary>
/// 是否主鍵
/// </summary>
public bool IsPrimaryKey { get; set; }

/// <summary>
/// 欄位名稱
/// </summary>
public string ColumnName { get; set; }

/// <summary>
/// 欄位型別
/// </summary>
public string ColumnType { get; set; }

/// <summary>
/// 資料庫型別對應的C#型別
/// </summary>
public string CSharpType
{
get
{
return SqlServerDbTypeMap.MapCsharpType(ColumnType);
}
}

/// <summary>
///
/// </summary>
public Type CommonType
{
get
{
return SqlServerDbTypeMap.MapCommonType(ColumnType);
}
}

/// <summary>
/// 位元組長度
/// </summary>
public int ByteLength { get; set; }

/// <summary>
/// 字元長度
/// </summary>
public int CharLength { get; set; }

/// <summary>
/// 小數位
/// </summary>
public int Scale { get; set; }

/// <summary>
/// 是否自增列
/// </summary>
public bool IsIdentity { get; set; }

/// <summary>
/// 是否允許空
/// </summary>
public bool IsNullable { get; set; }

/// <summary>
/// 描述
/// </summary>
public string Remark { get; set; }
}
#endregion

#region SqlServerDbTypeMap

public class SqlServerDbTypeMap
{
public static string MapCsharpType(string dbtype)
{
if (string.IsNullOrEmpty(dbtype)) return dbtype;
dbtype = dbtype.ToLower();
string csharpType = "object";
switch (dbtype)
{
case "bigint": csharpType = "long"; break;
case "binary": csharpType = "byte[]"; break;
case "bit": csharpType = "bool"; break;
case "char": csharpType = "string"; break;
case "date": csharpType = "DateTime"; break;
case "datetime": csharpType = "DateTime"; break;
case "datetime2": csharpType = "DateTime"; break;
case "datetimeoffset": csharpType = "DateTimeOffset"; break;
case "decimal": csharpType = "decimal"; break;
case "float": csharpType = "double"; break;
case "image": csharpType = "byte[]"; break;
case "int": csharpType = "int"; break;
case "money": csharpType = "decimal"; break;
case "nchar": csharpType = "string"; break;
case "ntext": csharpType = "string"; break;
case "numeric": csharpType = "decimal"; break;
case "nvarchar": csharpType = "string"; break;
case "real": csharpType = "Single"; break;
case "smalldatetime": csharpType = "DateTime"; break;
case "smallint": csharpType = "short"; break;
case "smallmoney": csharpType = "decimal"; break;
case "sql_variant": csharpType = "object"; break;
case "sysname": csharpType = "object"; break;
case "text": csharpType = "string"; break;
case "time": csharpType = "TimeSpan"; break;
case "timestamp": csharpType = "byte[]"; break;
case "tinyint": csharpType = "byte"; break;
case "uniqueidentifier": csharpType = "Guid"; break;
case "varbinary": csharpType = "byte[]"; break;
case "varchar": csharpType = "string"; break;
case "xml": csharpType = "string"; break;
default: csharpType = "object"; break;
}
return csharpType;
}

public static Type MapCommonType(string dbtype)
{
if (string.IsNullOrEmpty(dbtype)) return Type.Missing.GetType();
dbtype = dbtype.ToLower();
Type commonType = typeof(object);
switch (dbtype)
{
case "bigint": commonType = typeof(long); break;
case "binary": commonType = typeof(byte[]); break;
case "bit": commonType = typeof(bool); break;
case "char": commonType = typeof(string); break;
case "date": commonType = typeof(DateTime); break;
case "datetime": commonType = typeof(DateTime); break;
case "datetime2": commonType = typeof(DateTime); break;
case "datetimeoffset": commonType = typeof(DateTimeOffset); break;
case "decimal": commonType = typeof(decimal); break;
case "float": commonType = typeof(double); break;
case "image": commonType = typeof(byte[]); break;
case "int": commonType = typeof(int); break;
case "money": commonType = typeof(decimal); break;
case "nchar": commonType = typeof(string); break;
case "ntext": commonType = typeof(string); break;
case "numeric": commonType = typeof(decimal); break;
case "nvarchar": commonType = typeof(string); break;
case "real": commonType = typeof(Single); break;
case "smalldatetime": commonType = typeof(DateTime); break;
case "smallint": commonType = typeof(short); break;
case "smallmoney": commonType = typeof(decimal); break;
case "sql_variant": commonType = typeof(object); break;
case "sysname": commonType = typeof(object); break;
case "text": commonType = typeof(string); break;
case "time": commonType = typeof(TimeSpan); break;
case "timestamp": commonType = typeof(byte[]); break;
case "tinyint": commonType = typeof(byte); break;
case "uniqueidentifier": commonType = typeof(Guid); break;
case "varbinary": commonType = typeof(byte[]); break;
case "varchar": commonType = typeof(string); break;
case "xml": commonType = typeof(string); break;
default: commonType = typeof(object); break;
}
return commonType;
}
}
#endregion

#>

然後再建立一個T4模板xx.tt檔案。

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core.dll" #>
<#@ assembly name="System.Data.dll" #>
<#@ assembly name="System.Data.DataSetExtensions.dll" #>
<#@ assembly name="System.Xml.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Data" #>
<#@ import namespace="System.Data.SqlClient" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>
<#@ include file="ClassAuto.ttinclude"#>
<# var manager2 = new Manager(Host, GenerationEnvironment, true) { OutputPath = Path.GetDirectoryName(Host.TemplateFile)}; #>
<# 
#>

<# 
    foreach (var item in DBHelper.GetDbTables(Config.ConnectionString,Config.DbDatabase,null))
    {
       
 #>

 <# 
        manager2.StartBlock(item.TableName+".cs");
 #>
using System;
using System.Data;
using System.Data.SqlClient;
namespace NHibernateDemo
{
    /// <summary>
    /// 資料表實體類:<#= item.TableName #> 
    /// </summary>
    [Serializable()]
    public class <#= item.TableName #>
    {
<#
foreach(DbColumn column in DBHelper.GetDbColumns(Config.ConnectionString,Config.DbDatabase,item.TableName))
{
#>
       /// <summary>
       ///<#=column.ColumnName#>
       /// </summary>     
       public <#= column.CSharpType #><# if(column.CommonType.IsValueType && column.IsNullable){#>?<#}#> <#=column.ColumnName #> { get; set; }
        
<#}
#>
    }
}

<# manager2.EndBlock(); #>

<#
    }  
 #>     
   
<# manager2.Process(true); #>

<#+
    public class Config
    {
        public static readonly string ConnectionString="Data Source=knight;Integrated Security=true;Initial Catalog=NHDemo;";
        public static readonly string DbDatabase="NHDemo";
    }
#>

儲存.tt檔案就可以自動生成類檔案。