I have an object u1
of type User
class. User has an attribute name
. How can I pass in u1
to a T4 template ? I am trying to do something ver开发者_StackOverflow中文版y simple:
User u1 = new User("John");
Template script:
Hello <# u1.Name #>
We did something similar to this. Our solution has the following elements:
- Custom host
- Custom template base class ("inherits" property of template directive)
- Host specific templates ("hostspecific" property of template directive)
The custom host is aggregated by the object which the stencil itself wants to invoke methods on.
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;
}
}
}
Now, within any of our templates, we can invoke methods on the object who actually kicked off the processing using the custom host property, eg:
<# CustomHost.CallbackFxn(); #>
Also, we don't use T4 in VS or using a separate executable -- we link against the Microsoft.VisualStudio.TextTemplating.10.0 and Microsoft.VisualStudio.TextTemplating.Interfaces.10.0 assemblies.
EDIT
We're using T4 templates allow users to define their own plugins for use in a particular step in our product workflow. So user templates are uploaded to our system, and processed like this:
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
}
}
}
Do you wanna use it in your template? Then you need to add a reference to the assembly containing the type. You cannot simply pass existing reference to the T4 engine unless you host it yourself in some highly unorthodox fashion (I've never seen anyone atempting to do this). And even if you went as far and did that how would you run it? Where is this reference supposed to come from?
Using the type from within a T4 template is as easy as adding the reference or copy/paste it in the class stub <#+ /*stuff goes here*/ #>
.
T4 templates process in one step which so the template needs to get everything it needs during its run there for you don't really "Pass" anything into them. Since the Template is text I will generally just have some parameters the developer can set at the top of the file. If you need user input you can have a window popup but man that sounds annoying when generating code.
Another solution is to use a custom template that uses template substitution in your t4 template similar to how a lot of the installed templates work.
Assuming you want to have code in some application or tool that eventually runs the template, then using runtime (preprocessed) templates with either custom properties defined in a <#+ #> class feature block or the <#@ parameter #> directive should give you what you want.
Have a look at the docs here: http://msdn.microsoft.com/en-us/library/gg586944.aspx
Hey - You need to set values etc inside the T4 script, so the User
initialization must happen inside the <# #>
tag, which would turn you example into something similar to
<# User u1 = new User() { .Name = "John" } #>
Hello <# u1.Name #>
As mentioned above you may also need to import namespaces. I have used the statements below in the start of a TT-file to get access to System.Data:
<#@ import namespace="System.Data" #>
<#@ import namespace="System.Data.SqlClient" #>
<#@ assembly name="System.Data" #>
精彩评论