在开发过程种,很多时候我们会写很多相似度很高的代码,书写这些代码是有规律的,比如我们会根据数据表生成实体类,就像EF那样,会从表中自动生成实体类,如果我们写一个应用程序,读取数据库,然后生成一个.cs类文件,这个相对简单,如果在VS中能解决这个问题,不用单跑一个执行程序就happy了,那T4就是来干这件事的。T4Text Template Transformation Toolkit全称,不作更多的解释,VS中的T4详见https://msdn.microsoft.com/zh-cn/library/bb126445.aspx

简单看一个例子,就是把数据的表生成对应的实体类,在项目中创建一个“运行时文本模版”,扩展名是.tt,其实.tt中的代码是标准的C#代码,并键时.tt可以在设计时生成.cs文件,也可以运行时生成,本例是设计时生成,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
<#@ template debug= "false"  hostspecific= "true"  language= "C#"  #><#@ assembly name= "System.Core"  #><#@ assembly name= "System.Data"  #><#@ assembly name= "System.Configuration"  #><#@ assembly name= "System.Xml"  #><#@ import  namespace = "System.Linq"  #><#@ import  namespace = "System.Text"  #><#@ import  namespace = "System.Collections.Generic"  #><#@ import  namespace = "System.Data"  #><#@ import  namespace = "System.Data.SqlClient"  #><#@ import  namespace = "System.Configuration"  #><#@ import  namespace = "System.Xml"  #><#@ output extension= ".txt"  #>
  
  <# //数据库连接字符串
var  constr= "server=.;database=testdb;uid=sa;pwd=gsw123;" ;
//遍历实体类并生成
foreach  ( var  clas  in  BuildClass(constr))
{
      WriteFile(clas.Key,BuildContent(clas.Value));
      AddFileToProject(clas.Key);
}#>生成实体类成功! 
  
<#+ 
//组装实体类和它的命名空间
string  BuildContent( string  content)
{
return  @"
/*****************************
*作者:桂素伟                 
*时间:" +DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ")+@"             
*说明:按照数据库表自动生成的类 
******************************/
using  System;
using  System.Text;
namespace  "+Host.ResolveAssemblyReference(" $(ProjectName) ")+@"
{
"+
content
+ @"
}
" ;
  
}
//把生成的实体类文件添加到当前项目中
public  void  AddFileToProject( string  className)
{
      //获取当前项目的名称
      var  proName=Host.ResolveAssemblyReference( "$(ProjectName)" )+ ".csproj" ;
      XmlDocument xmlDoc =  new  XmlDocument();
      xmlDoc.LoadXml(System.IO.File.ReadAllText(Host.ResolveAssemblyReference( "$(ProjectDir)" )+ @"\" +proName,Encoding.Default));
      XmlNode root = xmlDoc.DocumentElement;
      if ( !root.InnerXml.Contains( @"Include="""  + className +  @".cs""" ))
      {    
         root.InnerXml +=  @"<ItemGroup><Compile Include=""" +className+ @".cs""></Compile></ItemGroup>" ;
         xmlDoc.Save(Host.ResolveAssemblyReference( "$(ProjectDir)" )+ @"\" +proName);
      }
  
}
//生成实体类文件
public  void  WriteFile( string  className, string  content)
{
      var  writer =  new  System.IO.StreamWriter(Host.ResolveAssemblyReference( "$(ProjectDir)" )+ @"\" +className + ".cs",  false , Encoding.Default);
      writer.Write(content);
      writer.Flush();
      writer.Close();
  
}
//从数据库获取实体类名和实体类源码
public   Dictionary< string , string > BuildClass( string  constr)
{
           
             using  ( var  con =  new  System.Data.SqlClient.SqlConnection(constr))
             {
                 var  cmd =  new  System.Data.SqlClient.SqlCommand();
                 cmd.CommandText =  "select name from sys.tables WHERE name!='sysdiagrams' AND name!='__MigrationHistory'" ;
                 cmd.Connection = con;
                 con.Open();
                 var  dr = cmd.ExecuteReader();
                 var  dt =  new  DataTable();
                 dt.Load(dr);
                 dr.Close();
  
                 var  dic = new  Dictionary< string string >();
                 foreach  (DataRow row  in  dt.Rows)
                 {
                     cmd.CommandText =  string .Format( @"select COLUMN_NAME,DATA_TYPE,CHARACTER_MAXIMUM_LENGTH,IS_NULLABLE
  from information_schema.columns WHERE TABLE_NAME = '{0}'" , row["name"].ToString());
                     var  fielddr = cmd.ExecuteReader();
                     var  fieldTable =  new  DataTable();
                     fieldTable.Load(fielddr);
                     fielddr.Close();
                     dic.Add(row[ "name" ].ToString(), CreateClass(row[ "name" ].ToString(), fieldTable));
                 }
                 return  dic;
             }
  
         }
   
     
         public  string  CreateClass( string  className, DataTable fields)
         {
             var  csBuilder =  new  StringBuilder( "\r\n" );
             csBuilder.AppendLine( string .Format( @"   public class {0}" , className));
             csBuilder.AppendLine( @" {" );
             foreach  (DataRow row  in  fields.Rows)
             {
                 csBuilder.AppendLine(CreateProperty(row));
                 
             }
             csBuilder.AppendLine( @"   }" );
             return  csBuilder.ToString();
         }
//生成属性
public  string  CreateProperty(DataRow row)
{
     var  dic =  new  Dictionary< string string >();
     dic.Add( "nvarchar" "string" );
     dic.Add( "varchar" "string" );
     dic.Add( "char" "string" );
     dic.Add( "text" "string" );
     dic.Add( "int" "string" );
     dic.Add( "money" "decimal" );
     dic.Add( "float" "float" );
     dic.Add( "bit" "bool" );
     var  csBuilder =  new  StringBuilder();
     csBuilder.AppendLine( string .Format( @"      public {0} {1}" , dic[row[ "DATA_TYPE" ].ToString().ToLower()], row[ "COLUMN_NAME" ].ToString()));
     csBuilder.AppendLine( @"      {" );
     csBuilder.AppendLine( string .Format( @"         get;" ));
     csBuilder.AppendLine( string .Format( @"         set;" ));
     csBuilder.AppendLine( @"      }" );
  
     return  csBuilder.ToString();
         #>