在T4模板中使用与该模板位于同一项目中的类型

22

我正在开发我的第一个T4代码生成工具,以添加一些存储过程帮助器代码到我的项目中。我创建了自定义类型(例如StoredProcedureStoredProcedureParameter),以帮助我的代码生成,并已在我的代码中包含了程序集和名称空间引用:

<#@ template debug="false" hostspecific="false" language="VB" #>
<#@ output extension=".generated.vb" #>
<#@ assembly name="$(TargetPath)" #>
<#@ import namespace="StoredProcCodeGenerator" #>

这使我可以在T4模板代码中使用自定义类型。然而,由于我的自定义类型存在于与T4模板代码相同的项目中,因此一旦运行模板代码,就无法重新编译我的项目而不重新启动Visual Studio。 这不太好玩。

我读了一篇很棒的文章,它通过使用T4 Toolbox来解决这个问题,但它没有起作用。 我可能实现了VolatileAssembly指令错误,或者T4工具箱只是未被安装。 我不确定工具箱是否已正确安装(我在Win XP上使用VS 2010)。

有哪些方法可以解决这个问题?


我不明白。在VS2010中,我一直使用T4模板,包括使用与模板相同项目中的类型,它运行得很好,并且每当我保存时重新运行模板 - 就像我期望的那样。 - Kirk Woll
@Kirk 我没有意识到在添加 <#@ VolatileAssembly ... 之前必须先删除 <#@ assembly name="$(TargetPath)" #>。我已经添加了一个答案来解释这个问题。 - Ben McCormack
1
有人能否将标题在开头处的“tempate”更改为“template”? - Maslow
@Maslow 哈哈!发现得真好。我可能已经看了50次标题,从来没有注意到那个。谢谢! - Ben McCormack
3个回答

10

您需要删除先前的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模板中使用在该项目中定义的类型。


当你两者都没有时会发生什么?(我既没有“assembly”也没有“VolatileAssembly”,但包含.tt文件的项目中的类对我始终是可访问的。) - Kirk Woll
@Kirk 很有趣。如果我不包含程序集引用,就会出现找不到我的自定义类型的错误。例如,我有一个在我的.tt文件中使用的“StoreProcedure”类型。如果我不引用我的程序集,它就无法找到“StoredProcedure”类型。 - Ben McCormack
2
Kirk,你或许在使用 VS2008 吗?在2008年,我们将项目引用的本地类型也作为标准搜索引入了。但在2010年我们移除了这一功能,以便您 T4 的世界与您实际构建的内容隔离开来。我们这样做是为了支持定位 .Net 2.0 或 3.5 项目,同时在模板中使用 .Net 4.0。 - GarethJ

1
希望这对您有所帮助,它展示了使用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


1

你也可以使用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。你如何将其用作指令? - William

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接