T4模板应用

Last updated on 9 months ago

T4模板应用(一)

之前在搭建系统框架的过程中用到的功能点,功效就是模板的功能。

个人是用T4模板批量生产到系统中的CRUD中,现在记录一下,不记录具体基础语法,只记录大概过程。

前提

ModelAuto.ttinclude

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
124
125
126
<#@ 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;
}
}
}
}#>

AllModelTemplate.tt

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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Data" #>
<#@ assembly name="$(TargetDir)Oracle.ManagedDataAccess.dll" #>
<#@ assembly name="System.Xml" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Data" #>
<#@ import namespace="Oracle.ManagedDataAccess" #>
<#@ import namespace="System.Collections.Generic"#>
<#@ include file="ModelAuto.ttinclude"#>
<# var manager2 = new Manager(Host, GenerationEnvironment, true) { OutputPath = Path.GetDirectoryName(Host.TemplateFile)}; #>
<#
ModelManager manager = new ModelManager();
List<string> list=manager.GetTableList();
#>

<#
foreach (var item in list)
{
string tableName=item;
DataTable table= manager.GetTableSchema(tableName);
#>

<#
manager2.StartBlock(tableName+".cs");
#>
using System;
namespace Model
{
/// <summary>
/// 数据表实体类:<#= tableName #>
/// </summary>
[Serializable()]
public class <#= tableName #>
{
<#
foreach(DataRow row in table.Rows)
{
#>
/// <summary>
/// <#=row["备注"]#>
/// </summary>
public <#= manager.TransFromSqlType(row["数据类型"].ToString())#> <#=row["字段名"]#>{ get; set; }
<#}
#>
}
}

<# manager2.EndBlock(); #>

<#
}
#>

<# manager2.Process(true); #>

<#+
public class ModelManager
{
/// <summary>
/// 数据库连接字符串
/// </summary>
private const string CONNECTION_STRING = "User ID=XLZF;Password=XLZF;Data Source=127.0.0.1/XE;";
/// <summary>
/// 用户信息表名
/// </summary>
private const string PERSONINFO_TABLE_NAME = "XLZF_USERS";
/// <summary>
/// 根据表名查询表结构信息
/// </summary>
private const string SELECT_SCHEMA_BY_TABLE_NAME = @"SELECT A.column_name 字段名, A.data_type 数据类型,
A.data_length 长度,
A.data_precision 整数位,
A.Data_Scale 小数位,
A.nullable 允许空值,
A.Data_default 缺省值,
B.comments 备注,
A.TABLE_NAME 表名
FROM user_tab_columns A,user_col_comments B
WHERE a.COLUMN_NAME = b.column_name
AND A.Table_Name = B.Table_Name
AND A.Table_Name = '{0}'";

/// <summary>
/// 获得数据连接
/// </summary>
/// <returns></returns>
private Oracle.ManagedDataAccess.Client.OracleConnection GetConnection()
{
return new Oracle.ManagedDataAccess.Client.OracleConnection(CONNECTION_STRING);
}


/// <summary>
/// 得到当前用户的所有表名
/// </summary>
/// <returns></returns>
public List<string> GetTableList()
{
string sql = "SELECT * FROM USER_TABLES";
DataTable dt = OracleHelper.ExecuteDataTable(sql);
List<string> list = new List<string>();
if (dt!=null&&dt.Rows.Count>0)
{
for (int i = 0; i < dt.Rows.Count; i++)
{
list.Add(dt.Rows[i]["TABLE_NAME"].ToString());
}
}
return list;
}

/// <summary>
/// 释放连接
/// </summary>
/// <param name="con"></param>
private void ReleaseConnection(Oracle.ManagedDataAccess.Client.OracleConnection 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 (Oracle.ManagedDataAccess.Client.OracleConnection con = GetConnection())
{
con.Open();

Oracle.ManagedDataAccess.Client.OracleCommand cmd = con.CreateCommand();

cmd.CommandText = string.Format(SELECT_SCHEMA_BY_TABLE_NAME,tableName);

cmd.CommandType = CommandType.Text;

Oracle.ManagedDataAccess.Client.OracleDataAdapter adapter = new Oracle.ManagedDataAccess.Client.OracleDataAdapter(cmd);

DataSet ds = new DataSet();

adapter.Fill(ds);

dt = ds.Tables[0];
}
return dt;
}

/// <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, "number", StringComparison.OrdinalIgnoreCase))
{
return "int";
}
if (string.Equals(type, "date", StringComparison.OrdinalIgnoreCase))
{
return "DateTime";
}
else if (string.Equals(type, "nvarchar2", StringComparison.OrdinalIgnoreCase))
{
return "string";
}

return "string";
}

}
#>

<#+
public class OracleHelper
{

private static string oracleConnectionStr = "User ID=XLZF;Password=XLZF;Data Source=127.0.0.1/XE;";


public static DataTable ExecuteDataTable(string sql, params Oracle.ManagedDataAccess.Client.OracleParameter[] paramList)
{
using (Oracle.ManagedDataAccess.Client.OracleConnection conn = new Oracle.ManagedDataAccess.Client.OracleConnection(oracleConnectionStr))
{
conn.Open();

using (Oracle.ManagedDataAccess.Client.OracleCommand command = conn.CreateCommand())
{
command.CommandText = sql;

command.Parameters.AddRange(paramList);

DataTable dt = new DataTable();

Oracle.ManagedDataAccess.Client.OracleDataAdapter adapter = new Oracle.ManagedDataAccess.Client.OracleDataAdapter(command);

adapter.Fill(dt);

return dt;
}
}
}

public static int ExecuteNonQuery(string sql, params Oracle.ManagedDataAccess.Client.OracleParameter[] paramList)
{
using (Oracle.ManagedDataAccess.Client.OracleConnection conn = new Oracle.ManagedDataAccess.Client.OracleConnection(oracleConnectionStr))
{
conn.Open();
using (Oracle.ManagedDataAccess.Client.OracleCommand command = conn.CreateCommand())
{
command.CommandText = sql;
command.Parameters.AddRange(paramList);

return command.ExecuteNonQuery();
}
}
}

public static object ExecuteScalar(string sql, params Oracle.ManagedDataAccess.Client.OracleParameter[] paramList)
{
using (Oracle.ManagedDataAccess.Client.OracleConnection conn = new Oracle.ManagedDataAccess.Client.OracleConnection(oracleConnectionStr))
{
conn.Open();
using (Oracle.ManagedDataAccess.Client.OracleCommand command = conn.CreateCommand())
{
command.CommandText = sql;
command.Parameters.AddRange(paramList);

return command.ExecuteScalar();
}
}
}
}
#>

运行

右键运行T4模板

image-20211205210429402

image-20211205210452146

结果

image-20211205210516763

image-20211205210543104

T4模板应用(二)

这次是通过 MMSQL 然后运行 T4 模板,生产数据库中所有表对应的实体类

前提

ModelAuto.ttinclude 与前面的一致。

编码

ModelTemplate.tt

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
<#@ template language="C#" debug="True" hostspecific="True" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Data" #>
<#@ assembly name="System.xml" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Data.SqlClient" #>
<#@ import namespace="System.Data" #>
<#@ include file="ModelAuto.ttinclude"#>
<# var manager = new Manager(Host, GenerationEnvironment, true) { OutputPath = Path.GetDirectoryName(Host.TemplateFile)}; #>
<#
string connectionString = "Data Source=.;Initial Catalog=XLZF_TESTDB;User ID=sa;Password=MyMMSQL1433...;";
SqlConnection conn = new SqlConnection(connectionString);
conn.Open();
System.Data.DataTable schema = conn.GetSchema("TABLES");
string selectQuery = "select * from @tableName";
SqlCommand command = new SqlCommand(selectQuery,conn);
SqlDataAdapter ad = new SqlDataAdapter(command);
System.Data.DataSet ds = new DataSet();

string propQuery = "SELECT 表名=sobj.name,字段名=scol.name,字段说明=sprop.[value] FROM syscolumns as scol inner join sys.sysobjects as sobj on scol.id=sobj.id and sobj.xtype='U' and sobj.name<>'dtproperties' left join sys.extended_properties as sprop on scol.id=sprop.major_id and scol.colid=sprop.minor_id where sobj.name='@tableName' and scol.name='@columnName'";
SqlCommand command2 = new SqlCommand(propQuery,conn);
SqlDataAdapter ad2 = new SqlDataAdapter(command2);
System.Data.DataSet ds2 = new DataSet();
#>

<#
foreach(System.Data.DataRow row in schema.Rows)
{ #>

<#
manager.StartBlock(row["TABLE_NAME"]+".cs");
#>
//----------<#=row["TABLE_NAME"].ToString()#>开始----------

using System;
namespace MyProject.Entities
{
/// <summary>
/// 数据表实体类:<#= row["TABLE_NAME"].ToString() #>
/// </summary>
[Serializable()]
public class <#= row["TABLE_NAME"].ToString() #>
{
<#
ds.Tables.Clear();
command.CommandText = selectQuery.Replace("@tableName",row["TABLE_NAME"].ToString());
ad.FillSchema(ds, SchemaType.Mapped, row["TABLE_NAME"].ToString());
foreach (DataColumn dc in ds.Tables[0].Columns)
{
#>
<#
ds2.Tables.Clear();
command2.CommandText = propQuery.Replace("@tableName",row["TABLE_NAME"].ToString());
command2.CommandText = command2.CommandText.Replace("@columnName",dc.ColumnName);
ad2.Fill(ds2);
#>
/// <summary>
/// <#= dc.DataType.Name #>:<#=ds2.Tables[0].Rows[0].ItemArray[2]#>
/// </summary>
public <#= dc.DataType.Name #> <#= dc.ColumnName #> {get;set;}
<# } #>
}
}

//----------<#=row["TABLE_NAME"].ToString()#>结束----------

<# manager.EndBlock(); #>

<#
} #>

<#
manager.Process(true);
#>

运行

常规右键-运行自定义工具

数据库

数据库表

运行结果

运行结果

image-20211205213257560

image-20211205213411337

T4模板应用(三)

本节主要描述给T4模板传参,应用T4模板生产指定的程序。

由于涉及到传参,先定义一个

参数实体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[Serializable]
public class ParameterEntity
{
/// <summary>
/// 命名空间
/// </summary>
public string NameSpace { get; set; }

/// <summary>
/// 作者
/// </summary>
public string Auth { get; set; }

/// <summary>
/// 表名
/// </summary>
public string TableName { get; set; }

/// <summary>
/// 连接字符串
/// </summary>
public string ConnectionString { get; set; }
}

ModelAuto.ttinclude

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
124
125
126
<#@ assembly name="System.Core"#>
<#@ assembly name="D:\2-技术实战\01-C#\04-Winform\DemoCode\packages\EnvDTE.8.0.2\lib\net10\EnvDTE.dll"#>
<#@ 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;
}
}
}
}#>

启动方法

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
public static void Mian_Two()
{
ParameterEntity parameterEntity = new ParameterEntity();

parameterEntity.Auth = "MR.XLZF";

parameterEntity.ConnectionString = "User ID=XLZF;Password=XLZF;Data Source=127.0.0.1/XLZF;";

parameterEntity.NameSpace = "T4ParameterSample";

parameterEntity.TableName = "CONFIGTABLE";

string T4File = "AllModelTemplate_1.tt";

string templateFileName = Path.GetFullPath(T4File);

CustomTextTemplatingEngineHost host = new CustomTextTemplatingEngineHost();

Engine engine = new Engine();

host.TemplateFileValue = templateFileName;

host.Session = new TextTemplatingSession();

host.Session.Add("parameter1", parameterEntity);

string input = File.ReadAllText(templateFileName);

string output = engine.ProcessTemplate(input, host);

Console.WriteLine(output);

foreach (CompilerError error in host.Errors)
{
Console.WriteLine(error.ToString());
}

Console.ReadLine();
}

CustomTextTemplatingEngineHost.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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.CodeDom.Compiler;
using System.IO;
using Microsoft.VisualStudio.TextTemplating;

namespace T4ParameterSample
{
public class CustomTextTemplatingEngineHost : ITextTemplatingEngineHost, ITextTemplatingSessionHost
{
#region ITextTemplatingEngineHost
//the path and file name of the text template that is being processed
//---------------------------------------------------------------------
internal string TemplateFileValue;
public string TemplateFile
{
get { return TemplateFileValue; }
}
//This will be the extension of the generated text output file.
//The host can provide a default by setting the value of the field here.
//The engine can change this value based on the optional output directive
//if the user specifies it in the text template.
//---------------------------------------------------------------------
private string fileExtensionValue = ".txt";
public string FileExtension
{
get { return fileExtensionValue; }
}
//This will be the encoding of the generated text output file.
//The host can provide a default by setting the value of the field here.
//The engine can change this value based on the optional output directive
//if the user specifies it in the text template.
//---------------------------------------------------------------------
private Encoding fileEncodingValue = Encoding.UTF8;
public Encoding FileEncoding
{
get { return fileEncodingValue; }
}
//These are the errors that occur when the engine processes a template.
//The engine passes the errors to the host when it is done processing,
//and the host can decide how to display them. For example, the host
//can display the errors in the UI or write them to a file.
//---------------------------------------------------------------------
private CompilerErrorCollection errorsValue;
public CompilerErrorCollection Errors
{
get { return errorsValue; }
}
//The host can provide standard assembly references.
//The engine will use these references when compiling and
//executing the generated transformation class.
//--------------------------------------------------------------
public IList<string> StandardAssemblyReferences
{
get
{
return new string[]
{
//If this host searches standard paths and the GAC,
//we can specify the assembly name like this.
//---------------------------------------------------------
//"System"

//Because this host only resolves assemblies from the
//fully qualified path and name of the assembly,
//this is a quick way to get the code to give us the
//fully qualified path and name of the System assembly.
//---------------------------------------------------------
typeof(System.Uri).Assembly.Location,
typeof(T4ParameterSample.Parameter).Assembly.Location
};
}
}
//The host can provide standard imports or using statements.
//The engine will add these statements to the generated
//transformation class.
//--------------------------------------------------------------
public IList<string> StandardImports
{
get
{
return new string[]
{
"System",
"T4ParameterSample"
};
}
}
//The engine calls this method based on the optional include directive
//if the user has specified it in the text template.
//This method can be called 0, 1, or more times.
//---------------------------------------------------------------------
//The included text is returned in the context parameter.
//If the host searches the registry for the location of include files,
//or if the host searches multiple locations by default, the host can
//return the final path of the include file in the location parameter.
//---------------------------------------------------------------------
public bool LoadIncludeText(string requestFileName, out string content, out string location)
{
content = System.String.Empty;
location = System.String.Empty;

//If the argument is the fully qualified path of an existing file,
//then we are done.
//----------------------------------------------------------------
if (File.Exists(requestFileName))
{
content = File.ReadAllText(requestFileName);
return true;
}
//This can be customized to search specific paths for the file.
//This can be customized to accept paths to search as command line
//arguments.
//----------------------------------------------------------------
else
{
return false;
}
}
//Called by the Engine to enquire about
//the processing options you require.
//If you recognize that option, return an
//appropriate value.
//Otherwise, pass back NULL.
//--------------------------------------------------------------------
public object GetHostOption(string optionName)
{
object returnObject;
switch (optionName)
{
case "CacheAssemblies":
returnObject = true;
break;
default:
returnObject = null;
break;
}
return returnObject;
}
//The engine calls this method to resolve assembly references used in
//the generated transformation class project and for the optional
//assembly directive if the user has specified it in the text template.
//This method can be called 0, 1, or more times.
//---------------------------------------------------------------------
public string ResolveAssemblyReference(string assemblyReference)
{
//If the argument is the fully qualified path of an existing file,
//then we are done. (This does not do any work.)
//----------------------------------------------------------------
if (File.Exists(assemblyReference))
{
return assemblyReference;
}
//Maybe the assembly is in the same folder as the text template that
//called the directive.
//----------------------------------------------------------------
string candidate = Path.Combine(Path.GetDirectoryName(this.TemplateFile), assemblyReference);
if (File.Exists(candidate))
{
return candidate;
}
//This can be customized to search specific paths for the file
//or to search the GAC.
//----------------------------------------------------------------
//This can be customized to accept paths to search as command line
//arguments.
//----------------------------------------------------------------
//If we cannot do better, return the original file name.
return "";
}
//The engine calls this method based on the directives the user has
//specified in the text template.
//This method can be called 0, 1, or more times.
//---------------------------------------------------------------------
public Type ResolveDirectiveProcessor(string processorName)
{
//This host will not resolve any specific processors.
//Check the processor name, and if it is the name of a processor the
//host wants to support, return the type of the processor.
//---------------------------------------------------------------------
if (string.Compare(processorName, "XYZ", StringComparison.OrdinalIgnoreCase) == 0)
{
//return typeof();
}
//This can be customized to search specific paths for the file
//or to search the GAC
//If the directive processor cannot be found, throw an error.
throw new Exception("Directive Processor not found");
}
//A directive processor can call this method if a file name does not
//have a path.
//The host can attempt to provide path information by searching
//specific paths for the file and returning the file and path if found.
//This method can be called 0, 1, or more times.
//---------------------------------------------------------------------
public string ResolvePath(string fileName)
{
if (fileName == null)
{
throw new ArgumentNullException("the file name cannot be null");
}
//If the argument is the fully qualified path of an existing file,
//then we are done
//----------------------------------------------------------------
if (File.Exists(fileName))
{
return fileName;
}
//Maybe the file is in the same folder as the text template that
//called the directive.
//----------------------------------------------------------------
string candidate = Path.Combine(Path.GetDirectoryName(this.TemplateFile), fileName);
if (File.Exists(candidate))
{
return candidate;
}
//Look more places.
//----------------------------------------------------------------
//More code can go here...
//If we cannot do better, return the original file name.
return fileName;
}
//If a call to a directive in a text template does not provide a value
//for a required parameter, the directive processor can try to get it
//from the host by calling this method.
//This method can be called 0, 1, or more times.
//---------------------------------------------------------------------
public string ResolveParameterValue(string directiveId, string processorName, string parameterName)
{
if (directiveId == null)
{
throw new ArgumentNullException("the directiveId cannot be null");
}
if (processorName == null)
{
throw new ArgumentNullException("the processorName cannot be null");
}
if (parameterName == null)
{
throw new ArgumentNullException("the parameterName cannot be null");
}
//Code to provide "hard-coded" parameter values goes here.
//This code depends on the directive processors this host will interact with.
//If we cannot do better, return the empty string.
return String.Empty;
}
//The engine calls this method to change the extension of the
//generated text output file based on the optional output directive
//if the user specifies it in the text template.
//---------------------------------------------------------------------
public void SetFileExtension(string extension)
{
//The parameter extension has a '.' in front of it already.
//--------------------------------------------------------
fileExtensionValue = extension;
}
//The engine calls this method to change the encoding of the
//generated text output file based on the optional output directive
//if the user specifies it in the text template.
//----------------------------------------------------------------------
public void SetOutputEncoding(System.Text.Encoding encoding, bool fromOutputDirective)
{
fileEncodingValue = encoding;
}
//The engine calls this method when it is done processing a text
//template to pass any errors that occurred to the host.
//The host can decide how to display them.
//---------------------------------------------------------------------
public void LogErrors(CompilerErrorCollection errors)
{
errorsValue = errors;
}
//This is the application domain that is used to compile and run
//the generated transformation class to create the generated text output.
//----------------------------------------------------------------------
public AppDomain ProvideTemplatingAppDomain(string content)
{
//This host will provide a new application domain each time the
//engine processes a text template.
//-------------------------------------------------------------

//return AppDomain.CurrentDomain;
return AppDomain.CreateDomain("Generation App Domain");
//This could be changed to return the current appdomain, but new
//assemblies are loaded into this AppDomain on a regular basis.
//If the AppDomain lasts too long, it will grow indefintely,
//which might be regarded as a leak.
//This could be customized to cache the application domain for
//a certain number of text template generations (for example, 10).
//This could be customized based on the contents of the text
//template, which are provided as a parameter for that purpose.
}

#endregion

#region ITextTemplatingSessionHost
public ITextTemplatingSession CreateSession()
{
return Session;
}

public ITextTemplatingSession Session
{
get;
set;
}
#endregion
}
}

AllModelTemplate_1.tt

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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
<#@ template debug="true" hostspecific="false" language="C#" #>
<#@ output extension=".txt" #>
<#@ assembly name="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Data.dll" #>
<#@ assembly name="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Data.OracleClient.dll" #>
<#@ assembly name="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Xml.dll" #>
<#@ assembly name="E:\Code\DemoCode\T4ParameterSample\bin\Debug\Oracle.ManagedDataAccess.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Data" #>
<#@ import namespace="Oracle.ManagedDataAccess" #>
<#@ import namespace="System.Collections.Generic"#>
<#@ include file="ModelAuto.ttinclude"#>
<#@ parameter name="parameter1" type="T4ParameterSample.ParameterEntity" #>
<#
ModelManager manager = new ModelManager();
manager.CONNECTION_STRING = parameter1.ConnectionString;
List<string> list=manager.GetTableList(parameter1.TableName);
#>

<#
foreach (var item in list)
{
string tableName=item;
DataTable table= manager.GetTableSchema(tableName);
#>
using System;
using System.Data;
using System.Data.OracleClient;
namespace <#= parameter1.NameSpace #>
{
/// <summary>
/// 数据表实体类:<#= tableName #>
/// By: <#= parameter1.Auth #>
/// </summary>
[Serializable()]
public class <#= tableName #>
{
<#
foreach(DataRow row in table.Rows)
{
#>
/// <summary>
/// <#=row["备注"]#>
/// </summary>
public <#= manager.TransFromSqlType(row["数据类型"].ToString())#> <#=row["字段名"]#>{ get; set; }
<#}
#>
}
}

<#
}
#>



<#+
public class ModelManager
{
/// <summary>
/// 数据库连接字符串
/// </summary>
public string CONNECTION_STRING = "User ID=XLZF;Password=XLZF;Data Source=127.0.0.1/XE;";
/// <summary>
/// 用户信息表名
/// </summary>
public string PERSONINFO_TABLE_NAME = "XLZF_USERS";
/// <summary>
/// 根据表名查询表结构信息
/// </summary>
public string SELECT_SCHEMA_BY_TABLE_NAME = @"SELECT A.column_name 字段名, A.data_type 数据类型,
A.data_length 长度,
A.data_precision 整数位,
A.Data_Scale 小数位,
A.nullable 允许空值,
A.Data_default 缺省值,
B.comments 备注,
A.TABLE_NAME 表名
FROM user_tab_columns A,user_col_comments B
WHERE a.COLUMN_NAME = b.column_name
AND A.Table_Name = B.Table_Name
AND A.Table_Name = '{0}'";

/// <summary>
/// 获得数据连接
/// </summary>
/// <returns></returns>
public Oracle.ManagedDataAccess.Client.OracleConnection GetConnection()
{
return new Oracle.ManagedDataAccess.Client.OracleConnection(CONNECTION_STRING);
}


/// <summary>
/// 得到当前用户的所有表名
/// </summary>
/// <returns></returns>
public List<string> GetTableList()
{
OracleHelper helper = new OracleHelper();

string sql = "SELECT * FROM USER_TABLES";

DataTable dt = helper.ExecuteDataTable(sql);

List<string> list = new List<string>();

if (dt!=null&&dt.Rows.Count>0)
{
for (int i = 0; i < dt.Rows.Count; i++)
{
list.Add(dt.Rows[i]["TABLE_NAME"].ToString());
}
}

return list;
}

/// <summary>
/// 得到当前用户的所有表名
/// </summary>
/// <returns></returns>
public List<string> GetTableList(string tableName)
{
string sql = "SELECT * FROM USER_TABLES WHERE TABLE_NAME = '"+tableName+"' ";

OracleHelper helper = new OracleHelper();

helper.oracleConnectionStr = CONNECTION_STRING;

DataTable dt = helper.ExecuteDataTable(sql);

List<string> list = new List<string>();

if (dt!=null&&dt.Rows.Count>0)
{
for (int i = 0; i < dt.Rows.Count; i++)
{
list.Add(dt.Rows[i]["TABLE_NAME"].ToString());
}
}

return list;
}

/// <summary>
/// 释放连接
/// </summary>
/// <param name="con"></param>
public void ReleaseConnection(Oracle.ManagedDataAccess.Client.OracleConnection 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 (Oracle.ManagedDataAccess.Client.OracleConnection con = GetConnection())
{
con.Open();

Oracle.ManagedDataAccess.Client.OracleCommand cmd = con.CreateCommand();

cmd.CommandText = string.Format(SELECT_SCHEMA_BY_TABLE_NAME,tableName);

cmd.CommandType = CommandType.Text;

Oracle.ManagedDataAccess.Client.OracleDataAdapter adapter = new Oracle.ManagedDataAccess.Client.OracleDataAdapter(cmd);

DataSet ds = new DataSet();

adapter.Fill(ds);

dt = ds.Tables[0];
}
return dt;
}

/// <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, "number", StringComparison.OrdinalIgnoreCase))
{
return "int";
}
if (string.Equals(type, "date", StringComparison.OrdinalIgnoreCase))
{
return "DateTime";
}
else if (string.Equals(type, "nvarchar2", StringComparison.OrdinalIgnoreCase))
{
return "string";
}

return "string";
}

}
#>

<#+
public class OracleHelper
{

public string oracleConnectionStr = "User ID=XLZF;Password=XLZF;Data Source=127.0.0.1/XLZF;";


public DataTable ExecuteDataTable(string sql, params Oracle.ManagedDataAccess.Client.OracleParameter[] paramList)
{
using (Oracle.ManagedDataAccess.Client.OracleConnection conn = new Oracle.ManagedDataAccess.Client.OracleConnection(oracleConnectionStr))
{
conn.Open();

using (Oracle.ManagedDataAccess.Client.OracleCommand command = conn.CreateCommand())
{
command.CommandText = sql;

command.Parameters.AddRange(paramList);

DataTable dt = new DataTable();

Oracle.ManagedDataAccess.Client.OracleDataAdapter adapter = new Oracle.ManagedDataAccess.Client.OracleDataAdapter(command);

adapter.Fill(dt);

return dt;
}
}
}

public int ExecuteNonQuery(string sql, params Oracle.ManagedDataAccess.Client.OracleParameter[] paramList)
{
using (Oracle.ManagedDataAccess.Client.OracleConnection conn = new Oracle.ManagedDataAccess.Client.OracleConnection(oracleConnectionStr))
{
conn.Open();
using (Oracle.ManagedDataAccess.Client.OracleCommand command = conn.CreateCommand())
{
command.CommandText = sql;
command.Parameters.AddRange(paramList);

return command.ExecuteNonQuery();
}
}
}

public object ExecuteScalar(string sql, params Oracle.ManagedDataAccess.Client.OracleParameter[] paramList)
{
using (Oracle.ManagedDataAccess.Client.OracleConnection conn = new Oracle.ManagedDataAccess.Client.OracleConnection(oracleConnectionStr))
{
conn.Open();
using (Oracle.ManagedDataAccess.Client.OracleCommand command = conn.CreateCommand())
{
command.CommandText = sql;
command.Parameters.AddRange(paramList);

return command.ExecuteScalar();
}
}
}
}
#>

结果

image-20211205223643141

结合传参功能的实现,在结合多种数据库,可以实现多种批量生产代码的功能,后续补充个人工具中的应用。


T4模板应用
http://example.com/2021/11/26/T4模板应用/
Author
Harris
Posted on
November 26, 2021
Licensed under