T4 输出多文件

最近在做一个小的网站项目,使用subsonic 做ORMapping的工具, 发现subsonic3已经在使用T4做代码生成工具了,相对于subsonic2开发效率上有了不小的提升,遗憾的是使用subsonic自带的T4模板不能为数据表生成各自的类文件,后来发现Damieng写了一片T4多输出的文章,http://damieng.com/blog/2009/01/22/multiple-outputs-from-t4-made-easy 介绍了如何让T4模板多输出,试了一下,可用。 步骤如下
1.新建写字板文件,贴进代码:

代码
<#@ 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"#>

<#+

// T4 Template Block manager for handling multiple file outputs more easily.
// Copyright (c) Microsoft Corporation. All rights reserved.
// This source code is made available under the terms of the Microsoft Public License (MS-PL)

// Manager class records the various blocks so it can split them up
class Manager
{
private struct Block {
public String Name;
public int Start, Length;
}

private List<Block> blocks = new List<Block>();
private Block currentBlock;
private Block footerBlock = new Block();
private Block headerBlock = new Block();
private ITextTemplatingEngineHost host;
private ManagementStrategy strategy;
private 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;
}
}
}
}#
>

 


保存文本文件,文件改名为manager.ttinclude


2. 修改ActiveRecord.tt

  1). 在ActiveRecord.tt 中引用 manager.ttinclude

    <#@ include file="Manager.ttinclude"#>

  2). 紧接着定义manager变量, outPutPath指定了生成文件保存的路径

    <# var manager = new Manager(Host, GenerationEnvironment, true) { OutputPath = Path.GetDirectoryName(Host.TemplateFile) }; #>

  3). Subsonic3采用一个循环生成所有表的ActiveRecord类,所以在循环体中插入代码块开始标识,"AR_"+tbl.ClassName+".cs" 指定了生成的类文件名为以 'AR_'开始的cs文件

        <# manager.StartBlock("AR_"+tbl.ClassName+".cs"); #> 

  4). 在循环结束处插入块结束标识

    <# manager.EndBlock(); #>

5). 循环体中的Activerecord类使用共同的命名空间引用,因此对命名空间的引用及目标文件的命名空间部分都放在startHead和endHead标识中间
  <# manager.StartHeader(); #>
  using System;

  using System.Collections.Generic;

  using System.Linq;

  ...

  ...
  namespace <#=Namespace #>

  {

  <# manager.EndHeader();#>


6). startFooter/endFooter标识之间只有一个}
  <# manager.StartFooter();#>

  }

  <# manager.EndFooter(); #>

7). 模板结尾处写上处理标识,true分离文件,false 不分开文件

<# manager.Process(true); #>

 

 

 

posted on 2010-11-12 16:15  黑头  阅读(1306)  评论(0编辑  收藏  举报

导航