C# T4模板和运行时参数

C# T4模板和运行时参数,c#,visual-studio-2010,t4,template-engine,C#,Visual Studio 2010,T4,Template Engine,我正在VS2010中构建一个插件,我被T4代卡住了。 现在,我已经实现了一个自定义T4主机(如MSDN所建议的)来生成我的T4结果,我使用它的方式如下: const string content = @"c:\Simple.tt"; var engine = new Engine(); var host = new MyTemplateHost(); var result = engine.ProcessTe

我正在VS2010中构建一个插件,我被T4代卡住了。 现在,我已经实现了一个自定义T4主机(如MSDN所建议的)来生成我的T4结果,我使用它的方式如下:

        const string content = @"c:\Simple.tt";
        var engine = new Engine();
        var host = new MyTemplateHost();            
        var result = engine.ProcessTemplate(File.ReadAllText(content), host);
        foreach (CompilerError error in host.Errors)
        {
            Console.WriteLine(error.ErrorText);
        }
直到我在模板中传递了一个参数,这才起作用。一旦我在.tt文件中创建了一个参数,主机就会说它不知道如何解决它。 我看到您可以使用TemplateSession来执行此操作,但我不知道如何将其传递给主机?
有没有更好的方法使用C#从.tt生成代码并在运行时传递参数?也许我走错了路。

在Visual Studio 2010中,T4模板引擎已经发生了根本性的变化。 现在,您可以直接运行模板文件并将所需的任何参数类型传递给它

        var template = Activator.CreateInstance<SimpleTemplate>();
        var session = new TextTemplatingSession();
        session["namespacename"] = "MyNamespace";
        session["classname"] = "MyClass";
        var properties = new List<CustomProperty>
        {
           new CustomProperty{ Name = "FirstProperty", ValueType = typeof(Int32) }
        };
        session["properties"] = properties;
        template.Session = session;
        template.Initialize();
var template=Activator.CreateInstance();
var session=new TextTemplatingSession();
会话[“名称空间名称”]=“MyNamespace”;
会话[“classname”]=“MyClass”;
var属性=新列表
{
新的CustomProperty{Name=“FirstProperty”,ValueType=typeof(Int32)}
};
会话[“属性”]=属性;
template.Session=会话;
初始化();
此语句将处理以下模板:

<#@ template language="C#" debug="true"  #>
<#@ output extension=".cs" #>
<#@ assembly name="System.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="SampleDomain.Entities" #>
<#@ parameter name="namespacename" type="System.String" #>
<#@ parameter name="classname" type="System.String" #>
<#@ parameter name="properties" type="System.Collections.Generic.List<CustomProperty>" #>

using System;
using System.Collections.Generic;
using SampleDomain.Entities;

namespace <#= this.namespacename #>
{
public class <#= this.classname #>

使用制度;
使用System.Collections.Generic;
使用SampleDomain.Entities;
名称空间
{
公共课

因此,老实说,不再需要主机了……

如果您正在为VS构建外接程序,您可能不需要自定义主机,而是可以通过其服务接口使用内置VS主机

作为核心服务API签出,您可以通过将DTE对象强制转换为IServiceProvider,然后调用GetService(typeof(STextTemplating))来获得该API


要传递参数,您可以将ITextTemplating对象侧置为,并将Session属性设置为的实现。Session本质上只是一个可序列化的属性包。这里提供了一个简单的属性包。

解决了这个问题。对于感兴趣的人,请参阅以下内容:


将ITextTemplatingSessionHost添加并实现到自定义主机。仅实现ITextTemplatingEngineHost不会为您提供会话支持

 [Serializable()]
    public class CustomCmdLineHost : ITextTemplatingEngineHost,ITextTemplatingSessionHost
    {

        ITextTemplatingSession session = new TextTemplatingSession();

        public ITextTemplatingSession CreateSession()
        {
            return session;
        }

        public ITextTemplatingSession Session
        {
            get
            {
                return session;
            }
            set
            {
                session = value;
            }
        }
请参阅(第节“在构造函数中传递参数”)

总结如下:

创建与TT文件同名的分部类

partial class MyWebPage
{
    private MyData m_data;
    public MyWebPage(MyData data) { this.m_data = data; }}
}
然后只需在类的构造函数中传递参数

MyWebPage page = new MyWebPage(data);
String pageContent = page.TransformText();

使用T4模板生成运行时

  • 如果需要在运行时生成代码,可以选择此方法。例如,您希望使用Selenium生成页面对象

  • 在解决方案中创建一个文件夹,将其命名为Templates(用于 T4模板)

  • 接下来添加一个T4类型的新项目,然后选择 运行时文本模板…我们将模板命名为MyNodeName.tt,如上图所示

  • 如下图所示添加代码,顶部由Visual Studio插入

  • 您可以看到我们想要传入名称空间和类名(它们是上面看到的Model.NameSpaceName和Model.ClassName标记)

    棘手的部分是学习如何传递参数

    使用文件名中的partial名称创建一个新的CS类

    但在类中,不要将其命名为MyNodeName部分将其命名为MyNodeName,如下所示:

       public partial class MyNodeName
        {
           public MyNodeNameModel Model { get; set; }
        }
    
    这与创建自己的分部类的TT文件(MyNodeName)的名称相同。但是现在请注意,我们有一个名为MODEL的值,该值属于此类类型

       public class MyNodeNameModel
        {
            public MyNodeNameModel()
            {
                ClassName = "Test";
            }
            public string ClassName { get; set; }
            public string NameSpaceName { get; set; }
        }
    
    模型类包含类名和名称空间名,以及您想要“注入”到模板中的任何内容

    此操作的键如图所示, 就是使用了运行时文本模板!如果使用文本模板,无论您做什么,都会看到类似“找不到模型”的错误或其他不明确的问题

    调试提示:
    “找不到模型”T4生成代码是否告诉您,在名为MODEL的变量为partial的类中,它找不到它!请检查partial和MODEL类型,以确保它们与在该文件夹中创建的任何其他正常类命名空间位于同一命名空间中。

    我在此处尝试了所有答案,以使其正常工作,但没有任何结果对于我的场景。最终我将以下内容添加到我的模板中

    <#@ assembly name="System.CodeDom" #>
    
    
    

    …然后成功了

    这段代码似乎假设使用预处理的模板(如果我理解您的SimpleTemplate类型。请参阅我的答案,了解如何使用内置VS主机进行此操作。非常有用,谢谢。在2012年也有效。调用
    Initialize()后
    您还可以检查
    模板。错误。HasErrors
    以查看类型是否正确传递:)谢谢,我明天必须在办公室试一试。Oleg在该页面上有很多详细信息…您能为自己的问题发布一个简洁的答案吗?找不到链接404