2011-05-23 33 views
3

我正在嘗試使用T4模板爲我們的系統生成遷移稍微容易一些。我無法弄清楚的一件事(這讓我想知道如果我使用T4模板來做錯誤的事情)是如何將渲染輸出複製到新文件中。我可以手動創建一個文件並複製生成的文件的內容,但這種情況在這裏違背了我的整個「讓事情變得更容易」的特點。將T4模板輸出複製到新文件

這是我的模板。在渲染時,理想情況下它將被複制到同一目錄中的「62-CreateWidgetsTable.cs」中。目標是創建一個我現在可以編輯的文件(我正在生成一個模板,換句話說,不生成完整的文件。)如果我可以在VS中重命名生成的文件(然後讓t4生成一個新模板,只會坐在那裏,直到有人出現並使用它),那就足夠好了。

<#@ template debug="false" hostspecific="false" language="C#" #> 
    <#@ output extension=".cs" #> 
    <# 
    var migrationNumber = "62"; 
    var migrationName = "CreateWidgetsTable"; 
    #> 
    using System; 
    using System.Collections.Generic; 
    using System.Linq; 
    using System.Text; 
    using Migrator.Framework; 

    namespace WidgetsIntl.Console.Migrations 
    { 
    [Migration(<#= DateTime.UtcNow.ToString("yyyyMMddhhmmss") #>)] 
    public class _<#= migrationNumber #>_<#= migrationName #> : Migration 
    { 
     public override void Up() 
     { 

     } 

     public override void Down() 
     { 

     } 
    } 
    } 
+0

是不是我的回答覆蓋問題了嗎? – benwasd 2011-05-26 10:08:50

+0

本!我會盡快嘗試一下。本週我很忙,所以我還沒有看過這個。我會盡管! – notJim 2011-05-26 15:13:05

+0

嘿,對不起,我發現了一個比你建議的更簡單的方法。儘管感謝您的幫助! – notJim 2011-07-07 18:49:04

回答

3

好吧,我想出了幾種方法可以做到這一點。最簡單的方法就是這樣做(我在之後找到的就是這樣做的),這裏是:t4 append output to existing file。關鍵信息是GenerationEnvironment是一個StringBuilder,其中包含運行模板的結果,因此您只需將該結果寫入您想要的任何舊文件!

另一種方法是使用T4 Toolbox。去下載吧!

然後,你就可以說創建擴展Template(由T4工具箱中定義)的一類,它覆蓋了一些默認行爲的模板:

<#@ template language="C#" hostspecific="True" debug="True" #> 
<#@ include file="T4Toolbox.tt" #> 
<# 
    // make an instance of the class we define below, set some variables, and render it 
    var tpl = new MyT4(); 
    tpl.MyVar = "Do those things you do"; 
    tpl.Render(); 
#> 
<#+ 
public class MyT4 : Template 
{ 
    public MyVar = "some stuff"; 

    public override string TransformText() 
    { 
     Output.PreserveExistingFile = true; // tells T4 that you want to manually edit this file afterward (for scaffoling, which was my use case) 
     Output.File = MyVar + ".cs"; // output will go in "some stuff.cs" 

     /****************** 
     Template is defined here! 
     *******************/ 
    #> 
    public class <#=myVar.Replace(" ", "_") #> 
    { 
     public void Method() 
     { 
      return "Hi, I am <#= myvar #>"; 
     } 
    } 
    <#+ 
     /************************* 
     now finishing up the TransformText() method 
     *************************/ 

     return GenerationEnvironment.ToString(); 
    } 
} 
#> 
3

在一些項目中,我使用了allready FileManager類。它基於這一博客帖子custominated實現:http://damieng.com/blog/2009/11/06/multiple-outputs-from-t4-made-easy-revisited

<#@ 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 
protected abstract class FileManager { 

    protected FileManager(ITextTemplatingEngineHost host, StringBuilder template) 
    { 
     this.host = host; 
     this.template = template; 
    } 

    protected abstract void CreateFile(String fileName, String content); 
    public abstract String GetCustomToolNamespace(String fileName); 
    public abstract String DefaultProjectNamespace { get; } 
    public abstract void Process(); 

    public static FileManager Create(ITextTemplatingEngineHost host, StringBuilder template) 
    { 
     return new VSManager(host, template); 
    } 

    protected class Block 
    { 
     public String Name; 
     public int Start, Length; 
    } 

    protected Block currentBlock; 
    protected List<Block> files = new List<Block>(); 
    protected Block footer = new Block(); 
    protected Block header = new Block(); 
    protected ITextTemplatingEngineHost host; 
    protected StringBuilder 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; 
    } 

    protected bool IsFileContentDifferent(String fileName, String newContent) 
    { 
     return !(File.Exists(fileName) && File.ReadAllText(fileName) == newContent); 
    } 

    protected Block CurrentBlock 
    { 
     get { return currentBlock; } 
     set { 
      if (CurrentBlock != null) 
       EndBlock(); 
      if (value != null) 
       value.Start = template.Length; 
      currentBlock = value; 
     } 
    } 

    // VS Manager 
    private class VSManager: FileManager 
    { 
     private EnvDTE.ProjectItem templateProjectItem; 
     private EnvDTE.DTE dte; 
     private List<string> generatedFileNames = new List<string>(); 

     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() 
     {   
      EndBlock(); 
      String headerText = template.ToString(header.Start, header.Length); 
      String footerText = template.ToString(footer.Start, footer.Length); 

      Directory.SetCurrentDirectory(Path.GetDirectoryName(host.TemplateFile)); 

      files.Reverse(); 
      foreach(Block block in files) 
      { 
       String fileName = Path.GetFullPath(block.Name); 
       String content = headerText + template.ToString(block.Start, block.Length) + footerText; 
       generatedFileNames.Add(fileName); 
       CreateFile(fileName, content); 
       template.Remove(block.Start, block.Length); 
      } 

      this.ProjectSync(generatedFileNames); 
      this.files = new List<Block>(); 
      this.footer = new Block(); 
      this.header = new Block(); 
      this.generatedFileNames = new List<string>(); 
     } 

     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 = host as IServiceProvider; 
      if (hostServiceProvider == null) 
      { 
       throw new ArgumentNullException("Could not obtain IServiceProvider"); 
      } 

      this.dte = (EnvDTE.DTE) hostServiceProvider.GetService(typeof(EnvDTE.DTE)); 
      if (this.dte == null) 
      { 
       throw new ArgumentNullException("Could not obtain DTE from host"); 
      } 
     } 

     private void ProjectSync(IEnumerable<string> keepFileNames) { 
      var projectFiles = new Dictionary<string, EnvDTE.ProjectItem>(); 

      foreach (string keepFileName in keepFileNames) 
      { 
       var item = this.dte.Solution.FindProjectItem(keepFileName); 
       if (item != null) 
       { 
        projectFiles.Add(keepFileName, item); 
       } 
      } 

      // Remove unused items from the project 
      /* foreach(var pair in projectFiles) // NEW 
      { 
       if (keepFileNames.Contains(pair.Key)) 
       { 
        pair.Value.Delete(); 
       } 
      } */ 

      // Add missing files to the project 
      foreach(string fileName in keepFileNames) 
      { 
       if (!projectFiles.ContainsKey(fileName)) 
       {  
        EnvDTE.Project targetProj = null; 
        foreach (EnvDTE.Project proj in this.dte.Solution.Projects) 
        { 
         if (string.IsNullOrEmpty(proj.FullName)) 
         { 
          continue; 
         } 

         if (fileName.Contains(Path.GetDirectoryName(proj.FullName) + @"\")) 
         { 
          targetProj = proj; 
          break; 
         } 
        }  

        var targetDir = NavigateTo(targetProj, fileName);  
        if (targetDir == null) 
        { 
         targetProj.ProjectItems.AddFromFile(fileName); 
         continue; 
        } 

        targetDir.ProjectItems.AddFromFile(fileName); 
       } 
      } 
     } 

     private void CheckoutFileIfRequired(String fileName) 
     { 
      var sc = dte.SourceControl; 
      if (sc != null && sc.IsItemUnderSCC(fileName) && !sc.IsItemCheckedOut(fileName)) 
      { 
       dte.SourceControl.CheckOutItem(fileName); 
      } 
     } 

     public EnvDTE.ProjectItem NavigateTo(EnvDTE.Project project, string path) 
     { 
      if (string.IsNullOrEmpty(project.FullName)) 
      { 
       return null; 
      } 

      var projBase = Path.GetDirectoryName(project.FullName); 
      var fileBase = Path.GetDirectoryName(path); 
      var naviBase = fileBase.Replace(projBase + @"\", ""); 

      if (string.IsNullOrEmpty(fileBase.Replace(projBase, ""))) 
      { 
       return null; 
      } 

      var naviPoints = naviBase.Split('\\'); 
      EnvDTE.ProjectItem item = null; 
      EnvDTE.ProjectItems items = project.ProjectItems; 

      foreach (var folder in naviPoints) 
      { 
       item = items.Item(folder); 
       items = item.ProjectItems; 
      } 

      return item; 
     } 
    } 
} #>