Reputation: 1024
I have a T4 Template that I am trying to pass object values to at runtime.
Basically what we're trying to to is:
From a Windows .NET form, read a file as text
Set an external object property to the text value
Access that object property in a T4 text template that has an output extension of .java.
I am starting very simple for now where I just have the template and the form and say an external class object:
Of course reading the text in the the form part and setting an object property like foo.foocode is fairly straightforward.
I just can't figure out how to access that object variable or property in the template and i've been looking at this for over a day..
Thanks
Upvotes: 6
Views: 9743
Reputation: 6500
Very old post but I keep forgetting how I want to do this in a bit more elegant way, so I post my solution here. This is simply another option, it is not designed to be better or faster.
For preprocessed <Name>.tt
files, the T4 engine creates an underlying <Name>.cs
and inside there is a class called <Name>
. So I wanted to have that flow of doing things, as I am working with simple objects and don't want to complicate things.
This is my main method
public static void Main(string[] args)
{
var outputFile = @"index.html";
var r = new Report();
// Here I pass whatever my "context" is, to the template
r.Values = new List<string> { "hello", "friendo" };
File.WriteAllText(outputFile, r.TransformText());
}
So this is very simple and all I need is to figure out, how I can make r.Values
work, because this property/field is not available per default.
Luckily the solution is also simple.
Here is my Report.tt
that creates a Report.cs
, which contains a Report
class:
<#@ template language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<!DOCTYPE html>
<html>
<head>
<title>Report</title>
</head>
<body>
<h1>Report</h1>
<table>
<tr>
<th>Variant</th>
</tr>
<# foreach (var variant in Values) { #>
<tr>
<td><#= variant #></td>
</tr>
<# } #>
</table>
</body>
</html>
<#+
#nullable enable
public List<string> Values { get; set; } = null!;
#nullable disable
#>
All I did was sneaking some C# code into the class generation. The important part is at the bottom, I added a property called Values
.
There can be any amount of properties and any available type can be used here. At the end, it is simply C# code.
This way I was able to pass fairly complex objects into the generation and they did not have to be serializable. And since I want the transformation during compile time anyway, this allows me to do it very straightforward.
Hope that helps future me and others 😁
Upvotes: 0
Reputation: 2110
At runtime you can only transform preprocessed templates, because the templating engine is not a redistributable part of Visual Studio. You can pass objects to a preprocessed templates using the parameter directive. The object type you pass to the template must be decorated with the SerializableAttribute
. Before calling the TransformText()
method put the value of the parameter into the templating session.
The output extension directive is ignored when using a preprocessed template. The TransformText()
method returns a string with the generated code. You can save it in whatever file type you want.
<#@ template debug="true" #>
<#@ parameter name="MyObject" type="MyNamespace.MyType" #>
<#
// now access the passed parameter using
this.MyObject
#>
Call the preprocessedTemplate:
var templateInstance = new MyTemplate();
templateInstance.Session = new Dictionary<string, object>();
templateInstance.Session.Add("MyObject", new MyType());
templateInstance.Initialize();
var generatedCode = templateInstance.TransformText();
System.IO.File.WriteAllText("outputfile.java", generatedCode);
Hope this helps.
Upvotes: 14