在通过T4模版引擎之基础入门 对T4有了初步印象后,我们开始实战篇。T4模板引擎可以当做一个代码生成器,代码生成器的职责当然是用来生成代码(这不是废话吗)。而这其中我们使用的最普遍的是根据数据库生成实体类。 工欲善其事必先利其器,在这之前先来介绍一
在通过T4模版引擎之基础入门 对T4有了初步印象后,我们开始实战篇。T4模板引擎可以当做一个代码生成器,代码生成器的职责当然是用来生成代码(这不是废话吗)。而这其中我们使用的最普遍的是根据数据库生成实体类。
工欲善其事必先利其器,在这之前先来介绍一款T4编辑器T4 Editor,我们可以点击链接去下载然后安装,不过还是推荐大家直接在VS扩展管理器里直接安装来的方便 工具->扩展管理器->联机库 搜索 "T4 Editor",选择第一项 "tangible T4 Editor 2.0 plus modeling tools for VS2010" 进行安装即可,如下图所示:
安装上T4 Editor后,编辑T4模板是就有代码着色和智能提示了,下图为安装T4 Editor后的代码着色效果,怎么样是不是耳目一新,呵呵
接下来开始正式进入我们的主题,从数据库自动生成实体类
新建一个控制台项目,然后添加T4模板,这里我们起名字为Customers.tt修改 输出文件扩展名为.cs
#@ 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" # >添加数据库操作DbHelper引用
DbHelper.ttinclude
public class DbHelper { #region GetDbTables public static List 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 " , database, tables); #endregion DataTable dt = GetDataTable(connectionString, sql); return dt.Rows.Cast ().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 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 ().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 /// /// 表结构 /// public sealed class DbTable { /// /// 表名称 /// public string TableName { get ; set ; } /// /// 表的架构 /// public string SchemaName { get ; set ; } /// /// 表的记录数 /// public int Rows { get ; set ; } /// /// 是否含有主键 /// public bool HasPrimaryKey { get ; set ; } } #endregion #region DbColumn /// /// 表字段结构 /// public sealed class DbColumn { /// /// 字段ID /// public int ColumnID { get ; set ; } /// /// 是否主键 /// public bool IsPrimaryKey { get ; set ; } /// /// 字段名称 /// public string ColumnName { get ; set ; } /// /// 字段类型 /// public string ColumnType { get ; set ; } /// /// 数据库类型对应的C#类型 /// public string CSharpType { get { return SqlServerDbTypeMap.MapCsharpType(ColumnType); } } /// /// /// public Type CommonType { get { return SqlServerDbTypeMap.MapCommonType(ColumnType); } } /// /// 字节长度 /// public int ByteLength { get ; set ; } /// /// 字符长度 /// public int CharLength { get ; set ; } /// /// 小数位 /// public int Scale { get ; set ; } /// /// 是否自增列 /// public bool IsIdentity { get ; set ; } /// /// 是否允许空 /// public bool IsNullable { get ; set ; } /// /// 描述 /// 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 # >
#@ include file ="$(ProjectDir)DbHelper.ttinclude" # >
DbHelper相对比较复杂,把一些常用操作进行了简单封装,因此放到一个单独的文件里面进行引用,可以方便的进行复用,这里DbHelper的后缀名使用ttinclude,这里的后缀名可以随便起,按照微软的建议: 用于include的文件尽量不要使用.tt做后缀名
在页面底部定义一些常用变量,以方便操作
public class config { public static readonly string ConnectionString= " Data Source=(local);Integrated Security=true;Initial Catalog=Northwind; " ; public static readonly string DbDatabase= " Northwind " ; public static readonly string TableName= " Customers " ; } # >
这里我们把数据库连接串和数据库、表名字定义一下,方便修改和使用
最后来编写用于实体类生成的代码
// ------------------------------------------------------------------------------ // // 此代码由T4模板自动生成 // 生成时间 by 懒惰的肥兔 // 对此文件的更改可能会导致不正确的行为,并且如果 // 重新生成代码,这些更改将会丢失。 // // ------------------------------------------------------------------------------ using System; namespace T4ConsoleApplication.Entities { public class { foreach (DbColumn column in DbHelper.GetDbColumns(config.ConnectionString, config.DbDatabase, config.TableName)){#> /// /// /// public if (column.CommonType.IsValueType && column.IsNullable){#>? { get ; set ; } } }全部完成后我们的Customers.tt文件就编写好了
Customers.tt
" false " hostspecific= " false " language= " C# " #> " .cs " #> " System.Core.dll " #> " System.Data.dll " #> " System.Data.DataSetExtensions.dll " #> " System.Xml.dll " #> namespace = " System " #> namespace = " System.Xml " #> namespace = " System.Linq " #> namespace = " System.Data " #> namespace = " System.Data.SqlClient " #> namespace = " System.Collections.Generic " #> namespace = " System.IO " #> " $(ProjectDir)DbHelper.ttinclude " #> // ------------------------------------------------------------------------------ // // 此代码由T4模板自动生成 // 生成时间 by 懒惰的肥兔 // 对此文件的更改可能会导致不正确的行为,并且如果 // 重新生成代码,这些更改将会丢失。 // // ------------------------------------------------------------------------------ using System; namespace T4ConsoleApplication.Entities { public class { foreach (DbColumn column in DbHelper.GetDbColumns(config.ConnectionString, config.DbDatabase, config.TableName)){#> /// /// /// public if (column.CommonType.IsValueType && column.IsNullable){#>? { get ; set ; } } } public class config { public static readonly string ConnectionString= " Data Source=(local);Integrated Security=true;Initial Catalog=Northwind; " ; public static readonly string DbDatabase= " Northwind " ; public static readonly string TableName= " Customers " ; } # >
进行保存后会自动生成Customers.cs文件
Customers.cs
// ------------------------------------------------------------------------------ // // 此代码由T4模板自动生成 // 生成时间 2012-07-18 17:51:26 by 懒惰的肥兔 // 对此文件的更改可能会导致不正确的行为,并且如果 // 重新生成代码,这些更改将会丢失。 // // ------------------------------------------------------------------------------ using System; namespace T4ConsoleApplication.Entities { public class Customers { /// /// /// public string CustomerID { get ; set ; } /// /// /// public string CompanyName { get ; set ; } /// /// /// public string ContactName { get ; set ; } /// /// /// public string ContactTitle { get ; set ; } /// /// /// public string Address { get ; set ; } /// /// /// public string City { get ; set ; } /// /// /// public string Region { get ; set ; } /// /// /// public string PostalCode { get ; set ; } /// /// /// public string Country { get ; set ; } /// /// /// public string Phone { get ; set ; } /// /// /// public string Fax { get ; set ; } } }
至此完整演示了怎样一步步根据数据库生成实体类的操作,是不是很简单,如对语法和操作不理解的地方可以参考T4模版引擎之基础入门,稍微用心研究下,轻松打造属于自己的代码生成器。
通过单个T4模板生成多个文件 ,以及 自动生成整个数据库的所有实体类 ,敬请期待
下班了,拍拍屁股走人。。。
源码:T4ConsoleApplication.rar