http://www.infoq.com/cn/news/2009/11/T4-Multiple-Output

初次认识并尝试使用T4生成代码的时候,相关学习资料似乎比较少。不过现在VS2010 的MSDN里已有相关章节,可参看《代码生成和文本模板》章节。可以用C#的语法写模板,实在舒服很多。

很快就发现T4难以生成多个文件的缺陷,微软似乎也不着急改进这一点。通过搜索,从InfoQ找到一篇文章《用T4生成多个文件》,链接到一篇文章,Damien Guard的扩展可以方便的生成多个文件。原文是英文,能看懂,然而如果翻译则斟酌字词太辛苦。

首先,保存以下代码为一个模板文件(例如保存文件名为Manager.ttinclude):

<#@ assembly name="System.Core"#>
<#@ assembly name="System.Data.Linq"#>
<#@ assembly name="EnvDTE"#>
<#@ assembly name="System.Xml"#>
<#@ assembly name="System.Xml.Linq"#>
<#@ import namespace="System"#>
<#@ import namespace="System.CodeDom"#>
<#@ import namespace="System.CodeDom.Compiler"#>
<#@ import namespace="System.Collections.Generic"#>
<#@ import namespace="System.Data.Linq"#>
<#@ import namespace="System.Data.Linq.Mapping"#>
<#@ import namespace="System.IO"#>
<#@ import namespace="System.Linq"#>
<#@ import namespace="System.Reflection"#>
<#@ import namespace="System.Text"#>
<#@ import namespace="System.Xml.Linq"#>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating"#>
<#+ // Manager class records the various blocks so it can split them up
class Manager {
private class Block {
public String Name;
public int Start, Length;
} private Block currentBlock;
private List<Block> files = new List<Block>();
private Block footer = new Block();
private Block header = new Block();
private ITextTemplatingEngineHost host;
private StringBuilder template;
protected List<String> generatedFileNames = new List<String>(); public static Manager Create(ITextTemplatingEngineHost host, StringBuilder template) {
return (host is IServiceProvider) ? new VSManager(host, template) : new Manager(host, template);
} public void StartNewFile(String name) {
if (name == null)
throw new ArgumentNullException("name");
CurrentBlock = new Block { Name = name };
} public void StartFooter() {
CurrentBlock = footer;
} public void StartHeader() {
CurrentBlock = header;
} public void EndBlock() {
if (CurrentBlock == null)
return;
CurrentBlock.Length = template.Length - CurrentBlock.Start;
if (CurrentBlock != header && CurrentBlock != footer)
files.Add(CurrentBlock);
currentBlock = null;
} public virtual void Process(bool split) {
if (split) {
EndBlock();
String headerText = template.ToString(header.Start, header.Length);
String footerText = template.ToString(footer.Start, footer.Length);
String outputPath = Path.GetDirectoryName(host.TemplateFile);
files.Reverse();
foreach(Block block in files) {
String fileName = Path.Combine(outputPath, block.Name);
String content = headerText + template.ToString(block.Start, block.Length) + footerText;
generatedFileNames.Add(fileName);
CreateFile(fileName, content);
template.Remove(block.Start, block.Length);
}
}
} protected virtual void CreateFile(String fileName, String content) {
if (IsFileContentDifferent(fileName, content))
File.WriteAllText(fileName, content);
} public virtual String GetCustomToolNamespace(String fileName) {
return null;
} public virtual String DefaultProjectNamespace {
get { return null; }
} protected bool IsFileContentDifferent(String fileName, String newContent) {
return !(File.Exists(fileName) && File.ReadAllText(fileName) == newContent);
} private Manager(ITextTemplatingEngineHost host, StringBuilder template) {
this.host = host;
this.template = template;
} private Block CurrentBlock {
get { return currentBlock; }
set {
if (CurrentBlock != null)
EndBlock();
if (value != null)
value.Start = template.Length;
currentBlock = value;
}
} private class VSManager: Manager {
private EnvDTE.ProjectItem templateProjectItem;
private EnvDTE.DTE dte;
private Action<String> checkOutAction;
private Action<IEnumerable<String>> projectSyncAction; public override String DefaultProjectNamespace {
get {
return templateProjectItem.ContainingProject.Properties.Item("DefaultNamespace").Value.ToString();
}
} public override String GetCustomToolNamespace(string fileName) {
return dte.Solution.FindProjectItem(fileName).Properties.Item("CustomToolNamespace").Value.ToString();
} public override void Process(bool split) {
if (templateProjectItem.ProjectItems == null)
return;
base.Process(split);
projectSyncAction.EndInvoke(projectSyncAction.BeginInvoke(generatedFileNames, null, null));
} protected override void CreateFile(String fileName, String content) {
if (IsFileContentDifferent(fileName, content)) {
CheckoutFileIfRequired(fileName);
File.WriteAllText(fileName, content);
}
} internal VSManager(ITextTemplatingEngineHost host, StringBuilder template)
: base(host, template) {
var hostServiceProvider = (IServiceProvider) host;
if (hostServiceProvider == null)
throw new ArgumentNullException("Could not obtain IServiceProvider");
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);
checkOutAction = (String fileName) => dte.SourceControl.CheckOutItem(fileName);
projectSyncAction = (IEnumerable<String> keepFileNames) => ProjectSync(templateProjectItem, keepFileNames);
} private static void ProjectSync(EnvDTE.ProjectItem templateProjectItem, IEnumerable<String> keepFileNames) {
var keepFileNameSet = new HashSet<String>(keepFileNames);
var projectFiles = new Dictionary<String, EnvDTE.ProjectItem>();
var originalFilePrefix = Path.GetFileNameWithoutExtension(templateProjectItem.get_FileNames()) + ".";
foreach(EnvDTE.ProjectItem projectItem in templateProjectItem.ProjectItems)
projectFiles.Add(projectItem.get_FileNames(), projectItem); // Remove unused items from the project
foreach(var pair in projectFiles)
if (!keepFileNames.Contains(pair.Key) && !(Path.GetFileNameWithoutExtension(pair.Key) + ".").StartsWith(originalFilePrefix))
pair.Value.Delete(); // Add missing files to the project
foreach(String fileName in keepFileNameSet)
if (!projectFiles.ContainsKey(fileName))
templateProjectItem.ProjectItems.AddFromFile(fileName);
} private void CheckoutFileIfRequired(String fileName) {
var sc = dte.SourceControl;
if (sc != null && sc.IsItemUnderSCC(fileName) && !sc.IsItemCheckedOut(fileName))
checkOutAction.EndInvoke(checkOutAction.BeginInvoke(fileName, null, null));
}
}
} #>

接着,在T4模板文件里引用这个模板,并声明一个Manager类实例,使用例子:

<#@ template language="C#" hostspecific="True"#>
<#@include file="Manager.ttinclude"#>
<# var manager = Manager.Create(Host, GenerationEnvironment); #>

使用两行代码可使代码输出到单独文件,你要输出的代码可写在这两个语句中间,StartNewFile的参数就是输出的文件名:

<# manager.StartNewFile("Employee.generated.cs"); #>  

<# manager.EndBlock(); #>  

比如可以这样写:

<# manager.StartNewFile("Employee.generated.cs"); #>
public class Employee { }
<# manager.EndBlock(); #>

还可以为每个输出文件输出同样的头部或顶部,只需要相应的语句:

<# manager.StartHeader(); #>
// Code generated by a template
using System;
<# manager.EndBlock(); #> <# manager.StartFooter(); #>
// It's the end
<# manager.EndBlock(); #>

最后使用这句来执行输出多个文件:

<# manager.Process(true); #>  

最新文章

  1. CANopen学习——PDO
  2. 供销大集-JS修改
  3. Javascript模块化编程(三):require.js的用法(转)
  4. sql 连接查询的区别 inner,left,right,full
  5. 二进制求最大公约数&amp;&amp;输出二进制
  6. HDOJ(1010)DFS+剪枝
  7. java集合类总结二
  8. The source attachment does not contain the source for the file SignatureParser.class错误
  9. ServletContentLIstener接口演示ServletContext的启动和初始化
  10. work5
  11. POJ 3666 Making the Grade (DP滚动数组)
  12. jQuery延迟加载(懒加载)插件 – jquery.lazyload.js-Web前端(W3Cways.com) - Web前端学习之路
  13. posgreSQL安装失败解决方案
  14. [Swift]LeetCode395. 至少有K个重复字符的最长子串 | Longest Substring with At Least K Repeating Characters
  15. PHP 【四】
  16. js循环json得到 键和值
  17. Django-rest-framework 接口实现 了解 GenericAPIView
  18. 设置DataGridView中表头颜色
  19. Redis进阶实践之四Redis的基本数据类型(转载4)
  20. 【转载】C#之玩转反射

热门文章

  1. Hat&#39;s Fibonacci
  2. [LeetCode] 221. Maximal Square _ Medium Tag: Dynamic Programming
  3. 怎么申请 bing api key
  4. LeetCode Python 位操作 1
  5. Java中将xml文件转化为json的两种方式
  6. html select 和dropdownlist小结收集
  7. html03
  8. DW课堂练习 用所学的知识去制作一个 (邮箱的注册页面)
  9. c#中WMI 中的日期和时间转为本地时间
  10. Let it Go