【问题标题】:pass an object to T4 template将对象传递给 T4 模板
【发布时间】:2011-05-31 18:48:28
【问题描述】:

我有一个 u1 类型的对象 User 类。用户有一个属性name。如何将u1 传递给 T4 模板?我正在尝试做一些非常简单的事情:

User u1 = new User("John"); 


模板脚本:

Hello <# u1.Name #>

【问题讨论】:

    标签: c# .net templates t4


    【解决方案1】:

    我们做了类似的事情。我们的解决方案包含以下要素:

    • 自定义主机
    • 自定义模板基类(模板指令的“继承”属性)
    • 主机特定模板(模板指令的“主机特定”属性)

    自定义主机由模板本身想要调用方法的对象聚合。

    interface ICallbackInterface
    {
        void CallbackFxn();
    }
    
    [Serializable]
    public class MyCustomHost : ITextTemplatingEngineHost, ITextTemplatingSessionHost, IStencilFileRecordManagement
    {
    
        private ICallbackInterface callback = null;
        public MyCustomHost(ICallbackInterface cb)
        {
            callback = cb;
        }
    
        public void CallbackFxn()
        {
            callback.CallbackFxn();
        }
    }
    
    public abstract class MyTemplateBase : TextTransformation
    {
        public virtual MyCustomHost CustomHost
        {
            get
            {
                dynamic metame = this;
                MyCustomHost rval = null;
                try
                {
                    /// <summary>
                    /// The "Host" property will be added to the generated class by the T4 environment whenever a 
                    /// "hostspecific" template is processed. 
                    /// </summary>
                    rval = metame.Host as MyCustomHost;
                }
                catch (RuntimeBinderException e)
                {
                    logger.ErrorException(
                        "Received the following exception while processing a stencil template", e);
    
                }
                return rval;
            }
        }
    }
    

    现在,在我们的任何模板中,我们都可以使用自定义主机属性调用实际启动处理的对象上的方法,例如:

    <# CustomHost.CallbackFxn(); #>
    

    此外,我们不使用 VS 中的 T4 或使用单独的可执行文件 - 我们链接到 Microsoft.VisualStudio.TextTemplating.10.0 和 Microsoft.VisualStudio.TextTemplating.Interfaces.10.0 程序集。

    编辑

    我们使用 T4 模板允许用户定义自己的插件,以便在我们的产品工作流程中的特定步骤中使用。所以用户模板被上传到我们的系统,并像这样处理:

    using Microsoft.VisualStudio.TextTemplating;
    
    class UserPluginWorkflowComponent : ICallbackInterface
    {
        public void CallbackFxn()
        {
            // invoked by user plugin
        }
    
        public void ExecuteUserPlugin()
        {
            MyCustomHost host = new MyCustomHost(this);
            host.TemplateFileValue = "UserPluginTemplateFilename";
            Engine engine = new Engine();
            string pluginResult = engine.ProcessTemplate(
                userPluginTemplateFileContents, 
                host);            
            if (!host.Errors.HasErrors)
            {
                // use pluginResult in some meaningful way
            }
        }
    }
    

    【讨论】:

    • 您能解释一下吗?展示对象如何“启动处理”?
    【解决方案2】:

    你想在你的模板中使用它吗?然后您需要添加对包含该类型的程序集的引用。您不能简单地将现有引用传递给 T4 引擎,除非您以某种非常非正统的方式自己托管它(我从未见过有人尝试这样做)。即使你走了这么远并且做到了,你会如何运行它?这个参考应该来自哪里?

    使用 T4 模板中的类型就像添加引用或将其复制/粘贴到类存根 &lt;#+ /*stuff goes here*/ #&gt; 中一样简单。

    【讨论】:

    • 我理解您所说的一些内容,但请您扩展一下:You cannot simply pass existing reference to the T4 engine unless you host it yourself 吗?另外,只是想让你知道,.tt 文件和用户类都是同一个解决方案的一部分
    • 对。那么没有办法交出参考。 T4 模板实际上在它自己的域中运行。有几个原因。如果您托管 T4 处理,您应该能够传递对处理的引用,但这不是我以前见过的任何人做过的事情。作为 T4 类存根的一部分,您最好的选择是引用包含您希望使用的东西的程序集或添加它。 Oleg Sych 关于 T4 的博客,在此处查看有关如何执行此操作的详细信息olegsych.com/2008/02/t4-assembly-directive
    【解决方案3】:

    T4 模板在一个步骤中处理,因此模板需要在其运行期间获得所需的一切,因为您不会真正将任何东西“传递”到它们中。由于模板是文本,我通常只会在文件顶部设置一些开发人员可以设置的参数。如果您需要用户输入,您可以弹出一个窗口,但是在生成代码时听起来很烦人。

    另一种解决方案是使用自定义模板,该模板在您的 t4 模板中使用模板替换,类似于许多已安装模板的工作方式。

    【讨论】:

    • 如你所知,我才刚刚开始,很多事情都在黑暗中。但是,最终,我想做的是创建模板的编译对象,并引用 user 类型的对象。当模板最终为run时,它会使用对象的属性来输出文本。
    【解决方案4】:

    假设您希望在某些应用程序或工具中拥有最终运行模板的代码,然后使用运行时(预处理)模板,其中包含在 类功能块中定义的自定义属性或 指令应该给你你想要的。

    在此处查看文档:http://msdn.microsoft.com/en-us/library/gg586944.aspx

    【讨论】:

    • 感谢您的回复。我们决定不使用 T4,因为它限制我们要么使用主机 VS 环境,要么为 T4 引擎调用外部 exec。规模是我们最关心的问题,每次调用都调用外部 exec,这是我们负担不起的操作。请让我知道是否有解决此问题的方法,因为我真的很喜欢 T4 的强大功能并且完全符合我们正在做的事情,但在这种情况下限制了我们。
    • 预处理模板不需要主机 VS。如果您的模板文本在构建时是固定的,那么它们应该可以满足您的需要。
    【解决方案5】:

    嘿 - 您需要在 T4 脚本中设置值等,因此 User 初始化必须发生在 &lt;# #&gt; 标记内,这会将您的示例变成类似于

         <# User u1 = new User() { .Name = "John" } #>
         Hello <# u1.Name #> 
    

    如上所述,您可能还需要导入命名空间。我在 TT 文件的开头使用了以下语句来访问 System.Data:

         <#@ import namespace="System.Data" #>
         <#@ import namespace="System.Data.SqlClient" #>
         <#@ assembly name="System.Data" #>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多