I have a fairly complex thing stuffed into a T4 template. Basically I take something like
{=foo=} more text...
and convert it into a class(view) like so:
public class MyView
{
public string foo{get;set;}
public string Write()
{
return foo+@" more text...";
}
}
The code generated is of course much more complicated than this. Anyway, the T4 template is over 600 lines of code right now and really becoming unmanageable fast. I believe the main problem is mixing code and "content"(ie, static code). I'm not really sure how to cleanly fix this problem though(while of course not affecting the code generated). I also don't see any feasible way to unit test my T4 code, other than simply testing it for T4 execution errors. And of course there seems to be the nearly impossible task of testing it's generated code.
Are there any "model-view" type frameworks or techniques I can use to make my T4 template code more clean?
IMHO the two most important concepts when writing complex templates is
Not as important but nice
Example separating model/view:
<#
// Model for Dependency Pooperties
Model = new []
{
new ClassDefinition ("MyDataGrid")
{
P ("CultureInfo" , "CultureInfo"),
P ("Pen" , "CellBorderPen"),
P ("IEnumerable<object>" , "Rows"),
C ("WColumnDefinition" , "Columns"),
},
};
#>
// Include the view
<#@ include file="..\T4\DependencyProperties.ttinclude" #>
The view then iterates over the model and generate the dependency properties.
The behavior of the dependency properties are then implemented as partial methods
partial void Changed_Columns(
ObservableCollection<WColumnDefinition> oldValue,
ObservableCollection<WColumnDefinition> newValue
)
{
HookUpColumns(oldValue, null);
HookUpColumns(newValue, this);
}
Note that putting this specific behavior into the model would significantly complicate the model.
Finally; it takes time even for a competent programmer to write metaprograms competently. It took me several attempts before I arrived at a style which I believe is maintainable but for me it was worth the effort as I am able to ship quality faster.
I hope this helps...
PS. I don't Think anyone would argue T4 is ever elegant but it's darn useful nonetheless.
After a long journey, I finally checked in the first unit tests to my T4 templates. Basically, what I did was abstract out a "view" (the actual T4 template), and the "logic" (what generates the code, but doesn't actually output it and doesn't rely on T4)
I then took this a step further and used a big hack to make it so that my logic file compiles outside of T4. This had the nice effect of making it so that intellisense and compiler errors worked. It also let me access my logic class from unit tests by simply referencing the project.
I wrote an article complete with code examples(before/after) and example unit tests on my blog, if you want the lengthy details on how to do it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With