前言

之前在 “使用T4模板生成代码 - 初探” 文章简单的使用了T4模板的生成功能,但对于一个模板生成多个实例文件,如何实现这个方式呢?无意发现一个解决方案 “MultipleOutputHelper.ttinclude” ,它让基于T4模板批量生成文件实例变得简单起来了。

什么是MultipleOutputHelper.ttinclude

Damien Guard是一个在加利福尼亚州圣何塞的开发人员,他构建出处理使用T4模板输出多文件的解决方案“MultipleOutputHelper.ttinclude”

使用

1. 初始化

获取MultipleOutputHelper.ttinclude文件模板

注意: 文件可以上github.com 托管上面获取(https://github.com/damieng/DamienGKit/tree/master/T4/MultipleOutputHelper

在T4模板中使用include指令导入MultipleOutputHelper.ttinclude文件或者将MultipleOutputHelper.ttinclude的代码复制在T4模板内。

然后初始化Manager对象,代码如下:

 注意: 这里的Manager.ttinclude 就是MultipleOutputHelper.ttinclude文件模板

2. 文件块

使用代码标识区分生成的代码块的范围

该代码声明了一个Employee.generated.cs文件,文件代码内容为:

1
public class Employee { ... }

3. 页眉和页脚

很多模板需要共享一个共同的页眉和页脚时,可以使用import语句进行打开和关闭。简单的使用StartHeader和StartFooter的代码方法进行分割。

4. 编译执行

使用Process方法,进行文件分割。

场景应用

基于之前的“使用T4模板生成代码 - 初探” 文章的场景,进行基于NHibernate Mapper 来获取Domain对象,然后进行批量生成多个代码文件。

1. 自定义T4模板,文件名为“EntityRepositoryTemplate.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
<#@ template  language="C#"    debug="true" hostspecific="True"#>
//  导入MultipleOutputHelper.ttinclude文件
<#@include file="$(SolutionDir)app\T4\MultipleOutputHelper.ttinclude"#>
// 导入相关的DLL
<#@ Assembly Name="$(SolutionDir)lib\SharpArch.2.0.2\NHibernate.dll" #>
<#@ Assembly Name="$(SolutionDir)lib\SharpArch.2.0.2\SharpArch.NHibernate.dll" #>
<#@ Assembly Name="$(SolutionDir)lib\SharpArch.2.0.2\SharpArch.Domain.dll" #>
<#@ Assembly Name="$(SolutionDir)lib\SharpArch.2.0.2\FluentNHibernate.dll" #>
<#@ Assembly Name="$(SolutionDir)app\Cotide.Data\bin\$(ConfigurationName)\Cotide.Infrastructure.dll" #>
<#@ Assembly Name="$(SolutionDir)app\Cotide.Core\bin\$(ConfigurationName)\Cotide.Domain.dll" #>
<#@ Assembly Name="$(SolutionDir)app\Cotide.Framework\bin\$(ConfigurationName)\Cotide.Framework.dll" #>
<#@ import namespace="System.IO"#>
<#@ import namespace="System"#>
<#@ import namespace="System.Configuration"#>  
 <# 
    // 初始化
    SharpArch.NHibernate.NHibernateSession.CloseAllSessions();
    SharpArch.NHibernate.NHibernateSession.Reset();
    string projectPath = @"C:\资料\Person\项目\Codeplex\电子商务\app\";
    string nhibernatePath = projectPath + @"Cotide.Web\NHibernate.config";
    string[] mappingAssemblies = new[] { @"C:\资料\Person\项目\Codeplex\电子商务\app\Cotide.Data\bin\Release\Cotide.Infrastructure.dll" };
    // 加载配置
    NHibernate.Cfg.Configuration configuration = SharpArch.NHibernate.NHibernateSession.Init(
    new SharpArch.NHibernate.SimpleSessionStorage(), mappingAssemblies,
    new Cotide.Infrastructure.NHibernateMaps.AutoPersistenceModelGenerator().Generate(),
    nhibernatePath);
    // 获取所有类映射
    var allClassMetadata = SharpArch.NHibernate.NHibernateSession.GetDefaultSessionFactory().GetAllClassMetadata();
    var manager = Manager.Create(Host, GenerationEnvironment); 
    foreach (var entry in allClassMetadata)
    {
       var entityName = entry.Value.EntityName.Split('.');
       var className = entityName[entityName.Length - 1];
        // 定义输出文件
        manager.StartNewFile("I"+className+"Repository.cs");
       #>//-------------------------------------------------------------------
//版权所有:版权所有(C) 2012,Microsoft(China) Co.,LTD
//系统名称:
//文件名称:I<#=className#>Repository.cs
//模块名称:
//模块编号:
//作  者:xcli
//创建时间:2013/4/6 12:49:50
//功能说明:
//-----------------------------------------------------------------
//修改记录:
//修改人:  
//修改时间:
//修改内容:
//----------------------------------------------------------------- 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Cotide.Domain.Contracts.Repositories.Extension;
namespace Cotide.Domain.Contracts.Repositories
{
        public interface I<#=className#>Repository : IDbProxyRepository<<#=className#>>
        {
 
        }
}
   <#
       // 结束输出文件
       manager.EndBlock();
    }
       // 执行编译
       manager.Process(true); 
    #>

输出文件效果:

让程序自动执行基于T4文件的编译工作

在MSDN-”演练:创建自定义文本模板宿主“ 文章里面看到一段代码,进行了T4文件的编译工作,代码如下:

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
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
using System;
using System.IO;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Text;
using Microsoft.VisualStudio.TextTemplating;
 
namespace CustomHost
{
    //The text template transformation engine is responsible for running
    //the transformation process.
    //The host is responsible for all input and output, locating files,
    //and anything else related to the external environment.
    //-------------------------------------------------------------------------
    class CustomCmdLineHost : 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
                };
            }
        }
        //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"
                };
            }
        }
        //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.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.
        }
    }
    //This will accept the path of a text template as an argument.
    //It will create an instance of the custom host and an instance of the
    //text templating transformation engine, and will transform the
    //template to create the generated text output file.
    //-------------------------------------------------------------------------
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                ProcessTemplate(args);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
        static void ProcessTemplate(string[] args)
        {
            string templateFileName = null;
            if (args.Length == 0)
            {
                throw new System.Exception("you must provide a text template file path");
            }
            templateFileName = args[0];
            if (templateFileName == null)
            {
                throw new ArgumentNullException("the file name cannot be null");
            }
            if (!File.Exists(templateFileName))
            {
                throw new FileNotFoundException("the file cannot be found");
            }
            CustomCmdLineHost host = new CustomCmdLineHost();
            Engine engine = new Engine();
            host.TemplateFileValue = templateFileName;
            //Read the text template.
            string input = File.ReadAllText(templateFileName);
            //Transform the text template.
            string output = engine.ProcessTemplate(input, host);
            string outputFileName = Path.GetFileNameWithoutExtension(templateFileName);
            outputFileName = Path.Combine(Path.GetDirectoryName(templateFileName), outputFileName);
            outputFileName = outputFileName + "1" + host.FileExtension;
            File.WriteAllText(outputFileName, output, host.FileEncoding);
 
            foreach (CompilerError error in host.Errors)
            {
                Console.WriteLine(error.ToString());
            }
        }
    }
}

联想:基于这个代码实例,可以做一个基于T4模板来批量输出代码的小工具,待续.....

参考资料

转自 http://www.cnblogs.com/K_tommy/archive/2013/04/06/T4.html

【转】- 使用T4模板批量生成代码的更多相关文章

  1. T4模板批量生成代码文件

    <#@ template debug="false" hostspecific="true" language="C#" #> ...

  2. T4模板批量生成代码

    大家通过比对下,就应该知道怎么玩. T4代码 <#@ template debug="false" hostspecific="true" languag ...

  3. FluentData-新型轻量级ORM 利用T4模板 批量生成多文件 实体和业务逻辑 代码

    FluentData,它是一个轻量级框架,关注性能和易用性. 下载地址:FlunenData.Model 利用T4模板,[MultipleOutputHelper.ttinclude]批量生成多文件 ...

  4. JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(四):自定义T4模板快速生成页面

    前言:上篇介绍了下ko增删改查的封装,确实节省了大量的js代码.博主是一个喜欢偷懒的人,总觉得这些基础的增删改查效果能不能通过一个什么工具直接生成页面效果,啥代码都不用写了,那该多爽.于是研究了下T4 ...

  5. 让T4脱离VS生成代码

    让T4脱离VS生成代码 最近项目快结束:空闲时间相对多一点:为了以后工作方便点:索性研究了VS的T4: 写个代码生成器:以后可以通过代码生成器调用项目里面的Dll直接生成代码或者xml: 应用以下两个 ...

  6. 黄聪:如何使用CodeSmith批量生成代码(转:http://www.cnblogs.com/huangcong/archive/2010/06/14/1758201.html)

    先看看CodeSmith的工作原理: 简单的说:CodeSmith首先会去数据库获取数据库的结构,如各个表的名称,表的字段,表间的关系等等,之后再根据用户自定义好的模板文件,用数据库结构中的关键字替代 ...

  7. T4 模板自动生成带注释的实体类文件

    T4 模板自动生成带注释的实体类文件 - 只需要一个 SqlSugar.dll 生成实体就是这么简单,只要建一个T4文件和 文件夹里面放一个DLL. 使用T4模板教程 步骤1 创建T4模板 如果你没有 ...

  8. 如何使用CodeSmith批量生成代码(原创系列教程)

    在上一篇我们已经用PowerDesigner创建好了需要的测试数据库,下面就可以开始用它完成批量代码生成的工作啦. 下面我会一步步的解释如何用CodeSmith实现预期的结果的,事先声明一下,在此只做 ...

  9. [转]黄聪:如何使用CodeSmith批量生成代码

    本文转自:http://www.cnblogs.com/huangcong/archive/2010/06/14/1758201.html 在上一篇我们已经用PowerDesigner创建好了需要的测 ...

随机推荐

  1. HTML 5 History API的”前生今世”

    History是有趣的,不是吗?在之前的HTML版本中,我们对浏览历史记录的操作非常有限.我们可以来回使用可以使用的方法,但这就是一切我们能做的了. 但是,利用HTML 5的History API,我 ...

  2. React Native中常见操作

    一.如何将项目安装到iOS手机上? 1.将手机于电脑连接好,并将Xcode打开连接上手机 2.在General  -> Team 下添加自己的Apple ID. 3.改本地的IP地址: 4.点击 ...

  3. UVA 113 Power of Cryptography (数学)

    Power of Cryptography  Background Current work in cryptography involves (among other things) large p ...

  4. OS X 10.10 apache配置

    配置内容转自:http://www.linuxidc.com/Linux/2015-04/116347.htm 一.apache的配置 apache已经自带了,只需如下三个命令就可以了. 开启apac ...

  5. Every student in every school should have the opportunity to learn to code

    “I think everybody in this country should learn how to program a computerbecause it teaches you how ...

  6. Java之姐妹素数

    所谓素数就是指相邻两个奇数均为素数, 判断一个数是否为素数的基本方法是:(以n=5为例) package com.cdp.SuShu; public class sushujisuan { publi ...

  7. 灰度直方算法 C++

    #include <string> #include "20140318计算类的面积.cpp" //////////////////////////////////// ...

  8. POD数据了解

    Plain old data (普通旧的数据); POD 是Plain Old Data的簡寫,是指一些系統的int, char, float.指標.array之類的資料型別,這應該蠻好想像的,就是C ...

  9. 正确编写Designated Initializer的几个原则

    Designated Initializer(指定初始化器)在Objective-C里面是很重要的概念,但是在日常开发中我们往往会忽视它的重要性,以至于我们写出的代码具有潜藏的Bug,且不易发现.保证 ...

  10. SP避免Form重复提交的三种方案

    SP避免Form重复提交的三种方案  1) javascript ,设置一个变量,只允许提交一次.   <script language="javascript">  ...