Скопируйте выходные данные шаблона T4 в новый файл - PullRequest
3 голосов
/ 24 мая 2011

Я пытаюсь использовать шаблоны 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()
      {

      }
    }
  }

Ответы [ 3 ]

4 голосов
/ 07 июля 2011

Хорошо, я нашел пару способов сделать это. Простейший способ сделать это (который я нашел только после , выполняя то, что я собираюсь показать вам) здесь: t4 добавляет вывод в существующий файл . Ключевая информация: 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 голосов
/ 24 мая 2011

В некоторых проектах я уже использую класс 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;
        }
    }
} #>
0 голосов
/ 01 июня 2018

Самый простой способ сделать это без плагинов - это щелкнуть правой кнопкой мыши по вашему целевому проекту и перейти к Add -> Existing Item и выбрать ваш сгенерированный файл.Это делает копию файла для вас в корневом каталоге проекта, который затем можно перемещать по мере необходимости.Это позволяет вам легко переносить сгенерированные файлы в проекты помимо того, в котором они были сгенерированы.

Я не проверял, что происходит, когда сам файл .tt находится в корне проекта, но это определенно работаетдо тех пор, пока .tt находится в подпапке (что в любом случае, вероятно, является хорошей практикой).

...