Talking to T4 – When NOT to MEF-ify
If you aren’t currently creating T4 templates, skip this post as a rather geeky exploration of something you should never have to touch. Hopefully in another few days you’ll harness to take care of this ickiness.
T4 templates should know their own output file name! This is not a function of the host or the harness or anything else. We want to wrap up the responsibility of creating a template in one location (with any redirection necessary by the class to get the job done and support single responsibility).
How to do this? My first idea was way nerdy wacko cool – use MEF! It’s new, it’s cool, everyone loves something with such a cute name!
The first step to MEF-ifywas moderately easy – add a class in the T4 template that has an export (or a property, or whatever). This just involves usual ickiness of setting T4 references, imports etc. Messy, but if you are working with T4 hosts and engines, you know this stuff. Got that done.
The code for the MEF export approach might look like:
public class TemplateSupport : IT4Template
public string GetFileName()
Then, comes the harder part – where do you deal with the MEF container? Some place in looking through this, I realized why this is NOT a good use for MEF. Two reasons actually – First I know exactly where the value should come from. I suppose I could argue for some sort of default service that uses an embedded naming pattern, but I already know that embedded naming patterns get ugly over time. XSLT will require a naming pattern (or call backs which are problematic during testing), but MEF and XML literals, etc will not. We need a single location, the template, to tell us the output file name. And it needs to run code to determine the name. I can’t predict for a pattern language everything someone will want to do, leaving people to extend a pattern service – well complexity goes up.
The second reason is that there is another way that is demonstrably less complex. To meet this bar, we need code simpler than what I showed above.
The second approach I considered was a value holder service for the host. Basically the template could request a service (which could the template itself) that supported the service and simply set the value. Then after the template ran, the engine could retrieve the filename from the host, which was holding it for everyone’s convenience:
<# (this.Host as IValueHolderService).SetValue("FileName", "Fred"); #>
That’s still pretty ugly. It occurred to me that we already have a value holder service, we just call it properties and it contains parameters to the T4 template. I’m just going to swipe a slot in the properties dictionary for my use, which by implication means you can’t use the property name I use. I decided it would not be nice to force you to avoid having a property named FileName with some other purpose than the output file name. The name needs to be exquisitely precise, because I am messing in your logical symbol space: TemplateOutputFileName does the trick for me (I’ll be happy to take other suggestions).
Now, you can just set the name of the output file in code, using whatever additional functionality you need. At the level of the T4 template code, you are just setting the value of a variable. The result is something like:
<# TemplateOutputFileName = "Fred"; #>
This fulfills my goal of a very simple way for you to set the filename inside your T4 template while allowing you to run any code you. There will be some T4 geeks that do a double take at the fact this is not a directive, but directives do not allow internal template code to run. If this gives people heartburn they can either declare the property (I won’t redeclare it) or I’ll also add support for a directive if people really want it.