开发者

T4 template and run-time parameters

I am building a plug-in in VS 2010 and I get stuck at the T4 generation. Right now I have implemented (like MSDN suggests) a custom T4 host to generate my T4 results and I use it in this way:

        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);
        }

This works until I pass a parameter in the Template. As soon as I create a parameter in the .tt file, the Host freak out saying that it doesn't know how to resolve it. I saw that you can use the TemplateSession to do th开发者_Python百科at but I didn't figure out how to pass it to my Host? Is there a better way of generating code from a .tt using C# and passing parameters at run-time? Maybe I am on the wrong path.


Within Visual Studio 2010 the T4 template engine has been radically changed. Now you can run directly a template file and pass to it any parameter type you want.

        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();

This statement will process the following template:

<#@ 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 #>

So honestly the host is not really needed anymore ...


If you are building an add-in for VS, you probably don't need a custom host, but can instead use the built-in VS host via its service interface.

Check out ITextTemplating as the core service API, which you can get by casting your DTE object to an IServiceProvider, then calling GetService(typeof(STextTemplating))

To pass parameters, you can then sidecast the ITextTemplating object to ITextTemplatingSessionHost and set the Session property to an implementation of ITextTemplatingSession. A session is essentially just a serializable property bag. There's a trivial one provided as TextTemplatingSession.


Add and implement the ITextTemplatingSessionHost to your custom host. Just implementing the ITextTemplatingEngineHost won't give you session support.

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

        ITextTemplatingSession session = new TextTemplatingSession();

        public ITextTemplatingSession CreateSession()
        {
            return session;
        }

        public ITextTemplatingSession Session
        {
            get
            {
                return session;
            }
            set
            {
                session = value;
            }
        }


Using T4 Templates for Run Time Generation

  1. You choose this method if you need to generate code at run-time. For example you want to generate a Page Object using Selenium.

    T4 template and run-time parameters

  2. Create a folder in your solution, name it Templates (good name for T4 Templates).

  3. Next add a new Item, of type T4, then pick the Runtime Text Template.... We named our template MyNodeName.tt which is seen in the image above.

  4. Add your code as shown below, the top part was inserted by Visual Studio...

T4 template and run-time parameters

You can see that we want to pass in the Namespace and ClassName (these are the Model.NameSpaceName and Model.ClassName markup seen above.

The tricky part is learning how to pass in the parameters...

Create a new CS class with the name partial in the file name.

T4 template and run-time parameters

But in the class don't name it MyNodeNamePartial name it MyNodeName like this:

   public partial class MyNodeName
    {
       public MyNodeNameModel Model { get; set; }
    }

This is the same name as the TT file. (MyNodeName) which creates it's own partial class. But now notice we have a value named MODEL of this class type..

   public class MyNodeNameModel
    {
        public MyNodeNameModel()
        {
            ClassName = "Test";
        }
        public string ClassName { get; set; }
        public string NameSpaceName { get; set; }
    }

The model class holds the ClassName and NameSpaceName and anything else you want to "inject" into the template.

The key to this working as shown, is that the Runtime Text Template was used! If you use a Text Template, no matter what you do, you will see errors similar to "Model not found" or other ambiguous issues.

Debugging Tips: "The Model cannot be found" is the T4 generation code telling you that in your partial class with the variable named MODEL, that it cannot find it! Check both your partial and the model types to ensure they are in same namespace as the any other normal class namespace would be if created in that folder.


Look at MSDN Reference (Section "Passing parameters in the constructor").

To summarize:

Create a partial class with the same name of your TT File.

partial class MyWebPage
{
    private MyData m_data;
    public MyWebPage(MyData data) { this.m_data = data; }}
}

Then simply pass your parameters in the constructor of the class

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


I tried all the answers here to get it to work and none worked for my scenario. Eventually I added the following to my template ...

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

.. and then it worked


Figured it out. For those interested see the following:

http://www.olegsych.com/2009/09/t4-preprocessed-text-templates/

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜