Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there anything out there to make T4 code more... clean?

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?

like image 425
Earlz Avatar asked Oct 29 '12 05:10

Earlz


2 Answers

IMHO the two most important concepts when writing complex templates is

  1. Separating the model and the view - This helps keeping the template logic to a minimum (often a cause of maintenance problem). Personally I don't think this requires a framework, it only requires that you do it.
  2. Partial is your friend - I use T4 in general to generate skeleton code from the model. Specific behavior might not be worth the effort to put in the model, better often to allow that behavior come in through the use of partial classes or methods.

Not as important but nice

  1. Make the code searchable - I don't rely on T4 Addons because I find none of them good enough, with the lack of IntelliSense in order to navigate the code I have to make the code searchable. It can be as simple as instead of calling a Column property Name, I call it ColumnName.
  2. Insert "tags" in the output - Makes it easier finding the code that generated that part of the output.

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.

like image 169
Just another metaprogrammer Avatar answered Oct 17 '22 22:10

Just another metaprogrammer


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.

like image 1
Earlz Avatar answered Oct 17 '22 23:10

Earlz