知羽模板生成器,从T4模板引擎到自己的代码生成器(1)

T4模板才接触半个月,但最不满的是它不能方便的脱离vs stuido。如果我要自己写代码生成器,怎么办?
园子里讲T4模板的不少,但讲如何在自己的程序中使用引擎的很少。查了很久的资料才知道,这东西叫:Text Template Host.
知道名字,资料就好了。接下来就可以试着建立一个自己的Host.
(1)写一个最简单的tt文件:
<#@ template debug="true" hostSpecific="true" #> <#@ output extension=".cs" #> using System;public class HelloWord{ static void Main() { } }
(2)新建一个项目,放个按钮,写点击代码:
TextCode host = new TextCode(); //host.Namespace = txtNamespace.Text; //host.Classname = txtTablemame.Text; host.FileExtension = ".cs"; host.TemplateFile = "Test.tt"; Microsoft.VisualStudio.TextTemplating.Engine engine = new Microsoft.VisualStudio.TextTemplating.Engine(); string content = ""; string input = ""; string output = ""; input = System.IO.File.ReadAllText(host.TemplateFile); output = engine.ProcessTemplate(input, host); string outputname ="newTest" + host.FileExtension; System.IO.File.WriteAllText(outputname, output); string strError=""; foreach (CompilerError error in host.Errors) { strError += error.ToString(); } MessageBox.Show(strError);
等等,一堆错误。。。那个TextCode 是什么东西?
(3) TextCode是自己写的一个类,它就是一个 host,需要实现一个TextTemplating.ITextTemplatingEngineHost接口。这个接口非常复杂,我找了一个比较完整的代码来解释。
using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Xml; using System.Data; using System.CodeDom.Compiler; namespace TestForT4.Code { [Serializable] class TextCode : Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost { //the path and file name of the text template that is being processed //--------------------------------------------------------------------- internal string TemplateFileValue; public string TemplateFile { get { return TemplateFileValue; } } //This will be the extension of the generated text output file. //The host can provide a default by setting the value of the field here. //The engine can change this value based _disibledevent=>//if the user specifies it in the text template. //--------------------------------------------------------------------- private string fileExtensionValue = ".txt"; public string FileExtension { get { return fileExtensionValue; } } //This will be the encoding of the generated text output file. //The host can provide a default by setting the value of the field here. //The engine can change this value based _disibledevent=>//if the user specifies it in the text template. //--------------------------------------------------------------------- private Encoding fileEncodingValue = Encoding.UTF8; public Encoding FileEncoding { get { return fileEncodingValue; } } //These are the errors that occur when the engine processes a template. //The engine passes the errors to the host when it is done processing, //and the host can decide how to display them. For example, the host //can display the errors in the UI or write them to a file. //--------------------------------------------------------------------- private CompilerErrorCollection errorsValue; public CompilerErrorCollection Errors { get { return errorsValue; } } //The host can provide standard assembly references. //The engine will use these references when compiling and //executing the generated transformation class. //-------------------------------------------------------------- public IList StandardAssemblyReferences { get { return new string[] { //If this host searches standard paths and the GAC, //we can specify the assembly name like this. //--------------------------------------------------------- //"System" //Because this host _disibledevent=>//fully qualified path and name of the assembly, //this is a quick way to get the code to give us the //fully qualified path and name of the System assembly. //--------------------------------------------------------- typeof(System.Uri).Assembly.Location }; } } //The host can provide standard imports or using statements. //The engine will add these statements to the generated //transformation class. //-------------------------------------------------------------- public IList StandardImports { get { return new string[] { "System" }; } } //The engine calls this method based _disibledevent=>//if the user has specified it in the text template. //This method can be called 0, 1, or more times. //--------------------------------------------------------------------- //The included text is returned in the context parameter. //If the host searches the registry for the location of include files, //or if the host searches multiple locations by default, the host can //return the final path of the include file in the location parameter. //--------------------------------------------------------------------- public bool LoadIncludeText(string requestFileName, out string content, out string location) { content = System.String.Empty; location = System.String.Empty; //If the argument is the fully qualified path of an existing file, //then we are done. //---------------------------------------------------------------- if (File.Exists(requestFileName)) { content = File.ReadAllText(requestFileName); return true; } //This can be customized to search specific paths for the file. //This can be customized to accept paths to search as command line //arguments. //---------------------------------------------------------------- else { return false; } } //Passes in the name of a service. If you have that service, you need to //pass back a pointer to it. Otherwise, you need to pass back NULL to //indicate that you have no knowledge of that service. //-------------------------------------------------------------------- public object GetHostOption(string optionName) { object returnObject; switch (optionName) { case "CacheAssemblies": returnObject = true; break; default: returnObject = null; break; } return returnObject; } //The engine calls this method to resolve assembly references used in //the generated transformation class project and for the optional //assembly directive if the user has specified it in the text template. //This method can be called 0, 1, or more times. //--------------------------------------------------------------------- public string ResolveAssemblyReference(string assemblyReference) { //If the argument is the fully qualified path of an existing file, //then we are done. (This does not do any work.) //---------------------------------------------------------------- if (File.Exists(assemblyReference)) { return assemblyReference; } //Maybe the assembly is in the same folder as the text template that //called the directive. //---------------------------------------------------------------- string candidate = Path.Combine(Path.GetDirectoryName(this.TemplateFile), assemblyReference); if (File.Exists(candidate)) { return candidate; } //This can be customized to search specific paths for the file //or to search the GAC. //---------------------------------------------------------------- //This can be customized to accept paths to search as command line //arguments. //---------------------------------------------------------------- //If we cannot do better, return the original file name. return ""; } //The engine calls this method based _disibledevent=>//specified in the text template. //This method can be called 0, 1, or more times. //--------------------------------------------------------------------- public Type ResolveDirectiveProcessor(string processorName) { //This host will not resolve any specific processors. //Check the processor name, and if it is the name of a processor the //host wants to support, return the type of the processor. //--------------------------------------------------------------------- if (string.Compare(processorName, "XYZ", StringComparison.OrdinalIgnoreCase) == 0) { //return typeof(); } //This can be customized to search specific paths for the file //or to search the GAC //If the directive processor cannot be found, throw an error. throw new Exception("Directive Processor not found"); } //A directive processor can call this method if a file name does not //have a path. //The host can attempt to provide path information by searching //specific paths for the file and returning the file and path if found. //This method can be called 0, 1, or more times. //--------------------------------------------------------------------- public string ResolvePath(string fileName) { if (fileName == null) { throw new ArgumentNullException("the file name cannot be null"); } //If the argument is the fully qualified path of an existing file, //then we are done //---------------------------------------------------------------- if (File.Exists(fileName)) { return fileName; } //Maybe the file is in the same folder as the text template that //called the directive. //---------------------------------------------------------------- string candidate = Path.Combine(Path.GetDirectoryName(this.TemplateFile), fileName); if (File.Exists(candidate)) { return candidate; } //Look more places. //---------------------------------------------------------------- //More code can go here... //If we cannot do better, return the original file name. return fileName; } //If a call to a directive in a text template does not provide a value //for a required parameter, the directive processor can try to get it //from the host by calling this method. //This method can be called 0, 1, or more times. //--------------------------------------------------------------------- public string ResolveParameterValue(string directiveId, string processorName, string parameterName) { if (directiveId == null) { throw new ArgumentNullException("the directiveId cannot be null"); } if (processorName == null) { throw new ArgumentNullException("the processorName cannot be null"); } if (parameterName == null) { throw new ArgumentNullException("the parameterName cannot be null"); } //Code to provide "hard-coded" parameter values goes here. //This code depends _disibledevent=>//If we cannot do better, return the empty string. return String.Empty; } //The engine calls this method to change the extension of the //generated text output file based _disibledevent=>//if the user specifies it in the text template. //--------------------------------------------------------------------- public void SetFileExtension(string extension) { //The parameter extension has a '.' in front of it already. //-------------------------------------------------------- fileExtensionValue = extension; } //The engine calls this method to change the encoding of the //generated text output file based _disibledevent=>//if the user specifies it in the text template. //---------------------------------------------------------------------- public void SetOutputEncoding(System.Text.Encoding encoding, bool fromOutputDirective) { fileEncodingValue = encoding; } //The engine calls this method when it is done processing a text //template to pass any errors that occurred to the host. //The host can decide how to display them. //--------------------------------------------------------------------- public void LogErrors(CompilerErrorCollection errors) { errorsValue = errors; } //This is the application domain that is used to compile and run //the generated transformation class to create the generated text output. //---------------------------------------------------------------------- public AppDomain ProvideTemplatingAppDomain(string content) { //This host will provide a new application domain each time the //engine processes a text template. //------------------------------------------------------------- return AppDomain.CreateDomain("Generation App Domain"); //This could be changed to return the current appdomain, but new //assemblies are loaded into this AppDomain _disibledevent=>//If the AppDomain lasts too long, it will grow indefintely, //which might be regarded as a leak. //This could be customized to cache the application domain for //a certain number of text template generations (for example, 10). //This could be customized based _disibledevent=>//template, which are provided as a parameter for that purpose. } } }
(4)最后呢。。。运行它!不过,如果你要想脱离vs。最好加上它的运行的dll.vs2008中叫Microsoft.VisualStudio.TextTemplating.dll。至于2010呢,两个文件,自己 gg一下吧。
运行,如果最后的提示是空白,那么就对啦,如果不对。请对着提示找错吧。
Tags:  旺旺代码生成器 动软代码生成器 代码生成器 t4模板 知羽模板生成器

延伸阅读

最新评论

发表评论