Luke 的个人资料lzcd照片日志列表更多 ![]() | 帮助 |
|
6月1日 The templating engine that no talks about… at least not in front of the childrenHere’s a little experiment for you to try in Visual Studio 2008:
Now if you save the file you should note that there’s a little “code behind” file hiding below the .tt file in much the same fashion as a WinForms or an ASP.Net control. And if we open it up we can see that it the result of the .tt file being executed as if it was a template:
So viola! We have a templating engine built right into Visual Studio and there you were just about to spend big dollars on a third party solution. Don’t you feel silly now? Well you shouldn’t feel quite so bad… yet… as the templating engine, T4 (as it’s officially known by Microsoft as) is all very nice but it’s not quite what I’m usually looking for in a templating engine. Quite often in such scenarios, what I want to do is grab some templates, grab some data and mix them all together en masse using a funky little console app to generate all sorts of weird and wonderful files for me. T4 just looks like it’s dealing with one file at a time and, even more annoyingly, I seem to need to be in Visual Studio to use it. This just won’t do. So how we do fix this? It just so happens that we can host the T4 scripting engine ourselves and do all sorts of weird and wonderful things with it. The “catch” is that if you do a search through the MSDN doco or related blogs on the T4 engine you’ll come across a boat load of API stuff that can all look rather intimidating. The good news is that we’re going to be producing a working host with little to no effort at all. (Cue fanfare) Presenting: Luke’s ultra quick starter guide to producing your own template crunching utility! In this example I’m going to be constructing a console app that takes a template file, a set of command line arguments and combines them together to produce a new file. It’s not exactly the most spectacular bit of technology around but it should hopefully get you on your way enough to produce you very own ultimate templating processing machine. Sooo…. the steps are as follows:
static void Main(string[] args) var commandLineArguments = new List<string>(args); var outputFilePath = Path.Combine(Path.GetDirectoryName(templateFilePath), Path.GetFileNameWithoutExtension(templateFilePath)); private static Host CreateHost(string templateFilePath, List<string> commandLineArguments) Most of this is quick and nasty throw away code (and not something I usually write… or at least admit to) so you can ignore most of it. The valuable bit that is worth noting is the code that creates an instance of T4’s template Engine class, feeds it a template file along with a mysterious thing called a host and magically churns out a brand new file from it. I’d suggest thinking of the Host as the middle man between your utility and the T4 templating engine… which brings us nicely to the next step which is…
[Serializable()] public bool LoadIncludeText(string requestFileName, out string content, out string location) if (!File.Exists(requestFileName)) content = File.ReadAllText(requestFileName); public object GetHostOption(string optionName) public string ResolveAssemblyReference(string assemblyReference) var candidate = Path.Combine(Path.GetDirectoryName(this.TemplateFile), assemblyReference); return string.Empty; public List<string> CommandLineArguments { get; set; } public Type ResolveDirectiveProcessor(string processorName) public string ResolvePath(string path) if (File.Exists(path)) var candidate = Path.Combine(Path.GetDirectoryName(this.TemplateFile), path); return path; public string ResolveParameterValue(string directiveId, string processorName, string parameterName) return string.Empty; public void SetFileExtension(string extension) public void LogErrors(CompilerErrorCollection errors) public AppDomain ProvideTemplatingAppDomain(string content) public void SetOutputEncoding(Encoding encoding, bool fromOutputDirective) } So now we have templates and a way to generate them. The thing that’s missing is a way to feed information from the outside world into the templates and the way we do this via Directive Processors. I’ll leave the fun of reading up about the specifics of these little beasties on MSDN as an exercise for the reader but here’s one I created earlier:
public class CustomDirectiveProcessor : DirectiveProcessor return false; public CodeDomProvider Provider { get; set; } public override void StartProcessingRun(CodeDomProvider languageProvider, string templateContents, CompilerErrorCollection errors) public static List<string> CommandLineArguments = new List<string>(); public override void ProcessDirective(string directiveName, IDictionary<string, string> arguments) if (string.Equals(directiveName, includeArgumentsKeyword, StringComparison.InvariantCultureIgnoreCase)) } private void GenerateCommandLineArgumentProperties(CodeGeneratorOptions options) property.GetStatements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(CommandLineArguments[argumentIndex]))); using (StringWriter writer = new StringWriter(codeBuffer, CultureInfo.InvariantCulture)) public override void FinishProcessingRun() public override string GetClassCodeForProcessingRun() public override string[] GetImportsForProcessingRun() public override string GetPreInitializationCodeForProcessingRun() public override string GetPostInitializationCodeForProcessingRun() public override string[] GetReferencesForProcessingRun() So what does this directive processor do exactly? Well I’m glad you asked. :) It allows us to extend the templating engine with our own functionality. In this case, we’re going to make it so that when some adds the following bit of code to their templates, we’re going to magically read in the command line arguments and supply them as properties: So the only thing left to is to try it out on a template…
Hopefully you should see a brand new cs file in the folder your textfile resides in with some content (not entirely useful content I’ll admit… but template driven content none the less) If you go back and examine the mysterious entity that is the CustomDirectiveProcessor class a little more thoroughly you may notice a few odd looking things:
So now you know how to harness the inbuilt goodness that is the T4 templating engine for your evil purposes. Oh… and before I forget: One last little party piece…
..and rerun your console app. If you’ve got Visual Studio kicking around you should now be looking at one fully fledged breakpoint. Yep you can break and step through your templates just like any other Visual Studio code. It even supports variable inspections. Pretty cool, no? Now all that’s left is to go forth and template yourself silly. Enjoy. 引用通告此日志的引用通告 URL 是: http://lzcd.spaces.live.com/blog/cns!B40A5E3E8E32C4EC!175.trak 引用此项的网络日志
|
|
|