我正在尝试使用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 :(得分:4)
好吧,我想出了几个方法来做到这一点。最简单的方法(我只在之后发现在我即将向你展示的方式之后发现它)在这里: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();
}
}
#>
答案 1 :(得分:3)
在某些项目中,我使用了下面的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;
}
}
} #>
答案 2 :(得分:0)
我发现没有插件的最简单方法是右键单击目标项目并转到Add - &gt;现有项目并选择生成的文件。这会在项目的根目录下为您创建文件的副本,然后您可以根据需要移动该文件。这使您可以轻松地将生成的文件传输到除生成它们之外的项目。
我还没有测试过.tt文件本身是项目根目录时会发生什么,但只要.tt在子文件夹中(这无论如何都是好的做法),这肯定有效。