使用与模板存在于同一项目中的 T4 模板中的类型
Posted
技术标签:
【中文标题】使用与模板存在于同一项目中的 T4 模板中的类型【英文标题】:Using types in a T4 template that exist in the same project as the template 【发布时间】:2011-03-25 09:40:19 【问题描述】:我正在开发我的第一个 T4 代码生成工具,以便将一些存储过程帮助程序代码添加到我的项目中。我创建了自定义类型(例如 StoredProcedure
和 StoredProcedureParameter
来帮助我生成代码,并在我的代码中包含了程序集和命名空间引用:
<#@ template debug="false" hostspecific="false" language="VB" #>
<#@ output extension=".generated.vb" #>
<#@ assembly name="$(TargetPath)" #>
<#@ import namespace="StoredProcCodeGenerator" #>
这允许我在我的 T4 模板代码中使用我的自定义类型。但是,因为我的自定义类型与 T4 模板代码存在于同一个项目中,所以在不重新启动 Visual Studio 的情况下运行模板代码后,我无法重新编译我的项目。这不是很有趣。
我阅读了great article,它通过使用 T4 工具箱解决了这个确切的问题,但它不起作用。要么我执行 VolatileAssembly
指令错误,要么根本没有安装 T4 工具箱。我不确定工具箱是否安装正确(我在 Win XP 上使用 VS 2010)。
有哪些方法可以解决这个问题?
【问题讨论】:
我不明白。在 VS2010 中,我一直使用 T4 模板,包括使用与模板在同一个项目中的类型,它工作得很好,并且每当我保存时都会重新运行模板——就像我所期望的那样。 @Kirk 我没有意识到在添加<#@ VolatileAssembly ...
之前我必须删除<#@ assembly name="$(TargetPath)" #>
。我添加了一个答案来解释它。
有人可以在开头编辑标题以说模板而不是模板吗?
@马斯洛哈!很棒的收获。我可能已经从标题开始了 50 次,但从未注意到这一点。谢谢!
【参考方案1】:
您需要删除之前的assembly
引用并然后添加VolatileAssembly
引用。如果您不先删除常规的 assembly
引用,则在添加 VolatileAssembly
引用时会收到一个错误,指出它已添加。
<#@ template debug="false" hostspecific="false" language="VB" #>
<#@ output extension=".generated.vb" #>
<#@ assembly name="$(TargetPath)" #>
<#@ VolatileAssembly processor="T4Toolbox.VolatileAssemblyProcessor"
name="$(TargetPath)" #>
<#@ import namespace="StoredProcCodeGenerator" #>
现在您可以继续构建您的项目并在您的 T4 模板中使用该项目中定义的类型。
【讨论】:
两者都没有会发生什么? (我既没有“程序集”也没有“VolatileAssembly”,并且项目中包含 .tt 文件的类始终可供我访问。) @Kirk 这很有趣。如果我不包含程序集引用,我会收到一个错误,指出它找不到我的自定义类型。例如,我在 .tt 文件中使用了一个StoreProcedure
类型。如果我不引用我的程序集,它将无法找到 StoredProcedure
类型。
Kirk,你可能在使用 VS2008 吗? 2008 年,我们也从项目引用中引入了本地类型作为标准搜索。我们在 2010 年将其移除,以便您的 T4 世界与您实际构建的世界隔离开来。我们这样做是为了在模板中使用 .Net 4.0 时支持定位 .Net 2.0 或 3.5 项目。【参考方案2】:
希望这是有帮助的,它显示了一个使用 volatileAssembly 的用例,我完全没有运气让这个 t4 模板工作,但我认为它可能会有所帮助:
// <autogenerated/>
// Last generated <#= DateTime.Now #>
<#@ template language="C#" hostspecific="true"#>
<#@ assembly name="System" #>
<#@ VolatileAssembly processor="T4Toolbox.VolatileAssemblyProcessor" name="bin\debug\FrameworkWpf.dll" #>
<#@ VolatileAssembly processor="T4Toolbox.VolatileAssemblyProcessor" name="bin\debug\FrameworkTestToolkit.dll" #>
<#@ VolatileAssembly processor="T4Toolbox.VolatileAssemblyProcessor" name="bin\debug\WpfAppTemplate.exe" #>
<#@ output extension=".cs" #>
<#@ import namespace="System" #>
<#@ import namespace="FrameworkTestToolkit" #>
namespace WpfAppTemplateTest
using System;
using System.Reflection;
<#
// Add new types into the below array:
Type[] types = new Type[]
typeof(FrameworkWpf.SafeEvent),
typeof(FrameworkWpf.Mvvm.ControllerBase),
typeof(FrameworkTestToolkit.PrivateAccessorGeneratorTestClass),
typeof(WpfAppTemplate.PostController),
typeof(WpfAppTemplate.ShellController),
;
// Do not modify this code
foreach (Type type in types)
PrivateAccessorGenerator builder = new PrivateAccessorGenerator(type, WriteLine, Error, Warning);
builder.Generate();
#>
来自http://blog.rees.biz/Home/unit-testing-and-private-accessors2
【讨论】:
【参考方案3】:您也可以使用EnvDte 遍历代码:
<#@ template language="C#" hostspecific="True" debug="True" #>
<#@ output extension="cs" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="Microsoft.VisualStudio.Shell.Interop.8.0" #>
<#@ assembly name="EnvDTE" #>
<#@ assembly name="EnvDTE80" #>
<#@ assembly name="VSLangProj" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="Microsoft.VisualStudio.Shell.Interop" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="EnvDTE80" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #><#
var serviceProvider = Host as IServiceProvider;
if (serviceProvider != null)
Dte = serviceProvider.GetService(typeof(SDTE)) as DTE;
// Fail if we couldn't get the DTE. This can happen when trying to run in TextTransform.exe
if (Dte == null)
throw new Exception("T4Generator can only execute through the Visual Studio host");
Project = GetProjectContainingT4File(Dte);
if (Project == null)
Error("Could not find the VS Project containing the T4 file.");
return"XX";
AppRoot = Path.GetDirectoryName(Project.FullName) + '\\';
RootNamespace = Project.Properties.Item("RootNamespace").Value.ToString();
Console.WriteLine("Starting processing");
ProcessFileCodeModel(Project);
#>
我已经在http://imaginarydevelopment.blogspot.com/2010/11/static-reflection-or-t4-with-envdte.html发布了更多以此为基础的代码
【讨论】:
但这只是将 RootNamespace 作为字符串文字。您如何将其用作指令?以上是关于使用与模板存在于同一项目中的 T4 模板中的类型的主要内容,如果未能解决你的问题,请参考以下文章
MSBuild 支持 Visual Studio 2017 RTM 中的 T4 模板
VS2022 可扩展性:如何解决“在 T4 模板执行中,‘Assembly 1’和‘Assembly2’中都存在类型‘XXX’