T4 - TT - 在TT文件中使用自定义类

13
我希望在我的TT文件中使用我自己在CS文件中定义的类。
示例:
public class ClassDefinition
{
    public string NameSpace { get; set; }
    public string Name { get; set; }
    public string Protection { get; set; }

    List<ClassProperty> Properties { get; set; }
}

我的TT看起来像:

<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>

<#@ assembly name="System" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Xml"#>

<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>

<#@ include file="$(ProjectDir)ClassDefinition.cs" #>

<#

// Read the model file
XmlDocument doc = new System.Xml.XmlDocument();
doc.Load(this.Host.ResolvePath("GeneratedXmlFile.xml"));

IList<XmlNode> nodeList = new List<XmlNode>();
foreach (XmlNode node in doc.DocumentElement)
{
    switch(node.Name)
    {
        case "Model": 
        {
            ClassDefinition classDefinition = new ClassDefinition();

但是我遇到了这个错误信息:

编译转换:找不到类型或命名空间名称“ClassDefinition”(是否缺少using指令或程序集引用?)

我在网上查过并尝试了以下方法: - 使用include - 使用assembly - 使用USING 但是都没有起作用。

有什么想法吗?


这可能看起来是一个显而易见的问题,但是你的ClassDefinition.cs文件是否与*.tt文件在同一个项目中? - Thundter
是的,它在同一个项目中! - Eagle
为什么不将ClassDefinition对象移动到一个新的/不同的dll项目中。将该项目添加到您的引用中,并将输出的dll导入到您的模板中? - Thundter
这是项目的结构。我将类定义文件从项目中分离出来,现在出现了错误。有什么想法吗? - Eagle
无法从截图中判断。可能是 T4 模板中您的代码存在语法错误。 - Matt Ward
5个回答

17

以下是完整的解决方案:

  1. 将这些类分离到另一个项目中

  2. 通过TT通过引用将这些类包含进来

<#@ assembly name="$(TargetDir)MyOwnLibraryProject.dll" #>
<#@ import namespace="MyOwnNamespace" #>
  • 不要忘记在您的TT项目中包含该库的参考

  • 您需要将MyOwnLibraryProject.dll复制到TT解决方案的BIN\DEBUG文件夹中

  • 魔法出现了 !!!

  • 每次更改您的DLL时,不要忘记将新版本放入文件夹中 :) 或者只需将您的库项目输出配置为与TT相同即可。 感谢您所有人提供指导和想法。


    太好了。我会记住这个,用在我的项目中。我一直在包含这些类。 - reckface

    8
    如果我理解正确,您正在尝试将一个类作为模板生成的一部分进行重用。
    该类本身需要在tt文件中,构建操作设置为none,自定义工具-无。我拥有一个模板管理器类,在顶部包含以下内容:
    <#@ template language="C#" #>
    <#@ assembly name="System.Core" #>
    <#@ import namespace="System.Linq" #>
    <#@ import namespace="System.Text" #>
    <#@ import namespace="System.Collections.Generic" #>
    <#@ import namespace="System.Diagnostics" #>
    
    <#+
    public class TemplateManager
    {
    

    然后在其他的t4模板中,我使用:
    <#@ include file="TemplateManager.tt"#>
    

    然后

    List<Values> values = TemplateManager.PrepareVariables(code, container, itemCollection.OfType<EntityType>())
    

    在你的情况下,ClassDefinition.tt文件将包含以下内容:
    <#@ template language="C#" #>
    <#@ assembly name="System.Core" #>
    <#@ import namespace="System.Linq" #>
    <#@ import namespace="System.Text" #>
    <#@ import namespace="System.Collections.Generic" #>
    <#@ import namespace="System.Diagnostics" #>
    
    <#+
    public class ClassDefinition
    {
        public string NameSpace { get; set; }
        public string Name { get; set; }
        public string Protection { get; set; }
    
        List<ClassProperty> Properties { get; set; }
    }
    #>
    

    然后您可以包含。
    <#@ include file="ClassDefinition.tt"#>
    

    1
    不,我不想将其包含在内,而是要在我的模板中使用它。 - Eagle

    3
    我本身也遇到了同样的问题 - 我的解决方法与 @Tehseen 的类似,但我提供了一个实际的带有解释的解决方案 :)
    在 T4 文件中包含任意 C#(您希望 T4 使用导入的 C# 而不是仅作为原始文本包含的部分)的技巧是隐藏 T4 将无法处理的 *.cs 文件的部分 - 例如 using 指令,并确保类型在 <#+ 块内声明 (而不是 <#)。
    这是我的解决方案:
    我的“入口点”MyScript.tt T4 脚本如下:
    <#@ template debug="true" hostspecific="false" language="C#" #>
    <#@ assembly name="System.Core" #>
    <#@ import namespace="System.Linq" #>
    <#@ import namespace="System.Text" #>
    <#@ import namespace="System.Text.RegularExpressions" #>
    <#@ import namespace="System.Collections.Generic" #>
    <#@ include file="IncludedCSFile.cs" #>
    <#@ output extension=".cs" #>
    <#
    
    MyClass foo = new MyClass(); // This is using a type from `IncludedCSFile.cs` in the T4 script.
    
    #>
    
    Hello, <#= foo.Name #>
    

    我的IncludedCSFile.cs看起来像这样:

    // <#+ /*
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text.RegularExpressions;
    
    namespace MyNamespace
    {
    
    // */
    
        public class MyClass
        {
            // etc...
        }
    }
    // #>
    

    解释:

    // <#+ /*
    
    • 初始的//会阻止主要的C#解析器(来自于工程)看到T4的<#+分隔符,否则会导致工程语法错误。
    • 然而,<#+会被T4解析器解析,并导致文件内部的C#代码被解释为T4脚本可以使用的代码。
    • 后面的/*开启了一个新的注释,使得T4的C#解析器忽略了using...语句和打开的namespace MyNamespace行,否则会导致T4语法错误。
      • 这是因为T4要求使用<#@ import namespace="" #>指令来表示using语句。
    // */
    
    • 这是第一个<#+后面的开放块注释的结束分隔符。
    • //*/隐藏在项目C#编译器中(该编译器无法看到/*),而T4的C#编译器将看到它,因为上一个/*覆盖了//
      • 这是因为在C#中,块注释会“注释掉”其他注释(如果有意义的话!)。
    // #>
    
    • 最后,T4要求在EOF之前加上T4块终止符,所以我们使用相同的前导//技巧来隐藏它,使C#看不到,而T4仍然可以看到。

    缺点:

    这种方法有一些缺点:

    • 前导的//会呈现到最终输出文件中。
      • 我认为这不能得到缓解。
      • 如果您知道解决方案,请编辑此答案或在评论回复中让我知道!
    • 包含的T4文件不能声明其自己的命名空间引用。
      • 虽然对于小型T4脚本来说,这不是问题,因为确保它们全部添加到入口点T4脚本中并不成问题。
      • 另一个解决方法是创建一个实际的*.ttinclude文件,其中只有必要的<#@ import namespace="" #>指令,然后包括*.cs文件。
    • 由于缺乏<#@ template #><#@ output #>指令,所以无法执行包含的文件作为其自己的T4文件,我了解必须将它们放在文件的开头。
      • 当然,大多数包含的*.ttinclude文件本身也无法执行。

    1
    将C#文件包含到T4模板中,使用以下代码:

    <#@ include file="$(ProjectDir)ClassDefinition.cs" #>
    

    将文本添加到T4模板的输出中。它不会编译类。
    您在T4模板中设置了debug=true,因此如果您查看%TEMP%目录,可以看到T4正在生成什么内容。运行T4模板时,您应该会在TEMP目录中看到一个.cs文件。在这个文件中,您会有类似以下的内容:
     this.Write("public class ClassDefinition\r\n{\r\n    public string NameSpace { get; set; }\r\n    p" +
           "ublic string Name { get; set; }\r\n    public string Protection { get; set; }\r\n\r\n " +
           "  List<ClassProperty> Properties { get; set; }\r\n}");
    

    你的C#类所发生的一切都只是在生成的T4输出中被写出。

    你可能想要做的是将ClassDefinition.cs文件包含在你的项目中,这样它就会作为你的项目的一部分进行编译。然后你可以引用包含ClassDefinition类的程序集。所以如果你的项目输出是MyLibrary.dll,并且其中包含了编译后的ClassDefinition.cs,那么你应该能够使用:

    <#@ assembly name="$(SolutionDir)$(OutDir)MyLibrary.dll" #>
    

    应当删除包含ClassDefinition.cs文件的那一行。

    1
    好的,这个解决方案正是我想要的。 TT和cs文件在同一个项目中。 我希望TT能够使用这些类来创建对象等等。 我会尝试这种方式并及时告知您。 - Eagle
    这是项目的结构。我将类定义文件从项目中分离出来。 现在我遇到了这个错误。有什么想法吗? - Eagle

    -1
    在你的导入语句之后添加这个应该可以解决问题:
    <#
    ClassDefinition cd = new ClassDefinition();
    #>
    

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