T4模板和运行时参数
我正在VS 2010中构建一个插件,我陷入了T4代。 现在我已经实现了(比如MSDN建议的)一个自定义T4主机来生成我的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(); var session = new TextTemplatingSession(); session["namespacename"] = "MyNamespace"; session["classname"] = "MyClass"; var properties = new List { new CustomProperty{ Name = "FirstProperty", ValueType = typeof(Int32) } }; session["properties"] = properties; template.Session = session; template.Initialize();
该语句将处理以下模板:
<#@ 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" #> using System; using System.Collections.Generic; using SampleDomain.Entities; namespace <#= this.namespacename #> { public class <#= this.classname #>
老实说,主人不再需要……
如果要为VS构建加载项,则可能不需要自定义主机,而是可以通过其服务接口使用内置VS主机。
查看ITextTemplating作为核心服务API,您可以通过将DTE对象转换为IServiceProvider,然后调用GetService(typeof(STextTemplating))来获取
要传递参数,您可以将ITextTemplating对象侧播到ITextTemplatingSessionHost ,并将Session属性设置为ITextTemplatingSession的实现。 会话本质上只是一个可序列化的属性包。 有一个简单的提供TextTemplatingSession 。
向您的自定义主机添加并实施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; } }
使用T4模板生成运行时
-
如果需要在运行时生成代码,请选择此方法。 例如,您想使用Selenium生成页面对象。
-
在解决方案中创建一个文件夹,将其命名为Templates(T4模板的好名称)。
-
接下来添加一个类型为T4的新项目,然后选择运行时文本模板….我们将模板命名为MyNodeName.tt,如上图所示。
-
添加您的代码如下所示,顶部部分由Visual Studio插入…
您可以看到我们要传递Namespace和ClassName(这些是上面看到的Model.NameSpaceName和Model.ClassName标记)。
棘手的部分是学习如何传递参数……
在文件名中创建名为partial的新CS类。
但是在类中没有将MyNodeNamePartial命名为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; } }
模型类包含ClassName和NameSpaceName以及您要“注入”模板的任何其他内容。
如图所示 , 这个工作的关键是使用了运行时文本模板! 如果您使用文本模板,无论您做什么,您都会看到类似于“未找到模型”或其他模糊问题的错误。
调试提示:“模型无法找到”是T4代码告诉你在你的部分类中使用名为MODEL的变量,它找不到它! 检查部分和模型类型,确保它们与在该文件夹中创建的任何其他普通类命名空间位于同一名称空间中。
查看MSDN参考 (“在构造函数中传递参数”一节)。
总结一下:
创建一个与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();
弄清楚了。 有兴趣的人请看以下内容:
http://www.olegsych.com/2009/09/t4-preprocessed-text-templates/