【问题标题】:Copy T4 template output to new file将 T4 模板输出复制到新文件
【发布时间】:2018-06-01 06:01:47
【问题描述】:

我正在尝试使用 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()
      {

      }
    }
  }

【问题讨论】:

  • 我的回答没有涵盖你的问题吗?
  • 本!我会尽快尝试。这周我很忙,所以我还没有看这个。不过我会的!
  • 嘿,抱歉,本,我找到了一种比你建议的更简单的方法。不过感谢您的帮助!

标签: file copy t4


【解决方案1】:

好的,我想出了几种方法来做到这一点。最简单的方法(我发现只有按照我即将向您展示的方式进行操作)是: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();
    }
}
#>

【讨论】:

    【解决方案2】:

    在一些项目中,我已经使用了下面的 FileManager 类。它是基于此博客文章的自定义实现: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;
            }
        }
    } #>
    

    【讨论】:

      【解决方案3】:

      我发现在没有插件的情况下执行此操作的最简单方法是右键单击您的目标项目并转到添加 -> 现有项目并选择您生成的文件。这会在项目的根目录为您创建一个文件副本,然后您可以根据需要移动该文件。这使您可以轻松地将生成的文件传输到生成它们的项目之外的项目中。

      我还没有测试当 .tt 文件本身位于项目的根目录中时会发生什么,但只要 .tt 位于子文件夹中,这肯定有效(无论如何这可能是一个好习惯)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-09
        • 1970-01-01
        相关资源
        最近更新 更多