转: http://www.wxzzz.com/1438.html
资料: https://cnsmartcodegenerator.codeplex.com/SourceControl/latest
什么是T4模板?
T4,即4个T开头的英文字母组合:Text Template Transformation Toolkit。T4文本模板将按照预期定义的一些代码进行动态生成一些所需的字符串内容。
如何动态编译T4模板?
通常我们在VS中进行*.tt (T4模板的编写),那么如何将t4模板代码动态的在我们自己的程序中进行编译生成呢?实际上微软已经提供了相应的接口进行。实现在自己的程序中进行编译T4模板需要实现一个自定义的宿主,你必须创建继承自 ITextTemplatingEngineHost 的类。接下来我们将一步一步完成我们自己的编译方式,(参考文章 http://msdn.microsoft.com/zh-cn/library/bb126579.aspx )
1 首先你必须要有的:Microsoft.VisualStudio.TextTemplating (点击下载并解压,获得 Microsoft.VisualStudio.TextTemplating.dll 文件)
2.在 Visual Studio 中,新建一个名为 CustomHost 的 C# 控制台应用程序。
3. 添加对下列程序集的引用
- Microsoft.VisualStudio.TextTemplating.*.0
- Microsoft.VisualStudio.TextTemplating.Interfaces.10.0 和更高版本
4. 用下面的代码替换 Program.cs 或 Module1.vb 文件中的代码:
- using System;
- using System.IO;
- using System.CodeDom.Compiler;
- using System.Collections.Generic;
- using System.Text;
- using Microsoft.VisualStudio.TextTemplating;
- namespace CustomHost
- {
- //The text template transformation engine is responsible for running
- //the transformation process.
- //The host is responsible for all input and output, locating files,
- //and anything else related to the external environment.
- //-------------------------------------------------------------------------
- class CustomCmdLineHost : 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 on the optional output directive
- //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 on the optional output directive
- //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<string> 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 only resolves assemblies from the
- //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<string> StandardImports
- {
- get
- {
- return new string[]
- {
- "System"
- };
- }
- }
- //The engine calls this method based on the optional include directive
- //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;
- }
- }
- //Called by the Engine to enquire about
- //the processing options you require.
- //If you recognize that option, return an
- //appropriate value.
- //Otherwise, pass back NULL.
- //--------------------------------------------------------------------
- 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 on the directives the user has
- //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 on the directive processors this host will interact with.
- //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 on the optional output directive
- //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 on the optional output directive
- //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 on a regular basis.
- //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 on the contents of the text
- //template, which are provided as a parameter for that purpose.
- }
- }
- //This will accept the path of a text template as an argument.
- //It will create an instance of the custom host and an instance of the
- //text templating transformation engine, and will transform the
- //template to create the generated text output file.
- //-------------------------------------------------------------------------
- class Program
- {
- static void Main(string[] args)
- {
- try
- {
- ProcessTemplate(args);
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- }
- }
- static void ProcessTemplate(string[] args)
- {
- string templateFileName = null;
- if (args.Length == 0)
- {
- throw new System.Exception("you must provide a text template file path");
- }
- templateFileName = args[0];
- if (templateFileName == null)
- {
- throw new ArgumentNullException("the file name cannot be null");
- }
- if (!File.Exists(templateFileName))
- {
- throw new FileNotFoundException("the file cannot be found");
- }
- CustomCmdLineHost host = new CustomCmdLineHost();
- Engine engine = new Engine();
- host.TemplateFileValue = templateFileName;
- //Read the text template.
- string input = File.ReadAllText(templateFileName);
- //Transform the text template.
- string output = engine.ProcessTemplate(input, host);
- string outputFileName = Path.GetFileNameWithoutExtension(templateFileName);
- outputFileName = Path.Combine(Path.GetDirectoryName(templateFileName), outputFileName);
- outputFileName = outputFileName + "1" + host.FileExtension;
- File.WriteAllText(outputFileName, output, host.FileEncoding);
- foreach (CompilerError error in host.Errors)
- {
- Console.WriteLine(error.ToString());
- }
- }
- }
- }
5. 仅对于 Visual Basic,打开“项目”菜单,单击“CustomHost 属性”。 在“启动对象”列表中单击“CustomHost.Program”。
6. 在“文件”菜单上,单击“全部保存”。
7. 在“生成”菜单上,单击“生成解决方案”。
进行编译并测试
若要测试自定义宿主,您需要编写一个文本模板,然后运行自定义宿主,将文本模板的名称传递给它并验证模板转换。
创建文本模板测试自定义宿主
1. 在C盘根目录中创建一个文本文件,将其命名为 TestTemplate.tt 。
可以使用任何文本编辑器(例如记事本)来创建文件。
2. 将以下内容添加到文件中:
- Text Template Host Test
- <#@ template debug="true" #>
- <# //Uncomment this line to test that the host allows the engine to set the extension. #>
- <# //@ output extension=".htm" #>
- <# //Uncomment this line if you want to debug the generated transformation class. #>
- <# //System.Diagnostics.Debugger.Break(); #>
- <# for (int i=0; i<3; i++)
- {
- WriteLine("This is a test");
- }
- #>
3. 保存并关闭文件。
完成/使用
进行使用测试吧,我们需要在cmd(命令提示符窗口)程序中进行测试。
1. 打开 cmd 窗口
2. 在窗口中键入 你的程序编译的文件路径CustomHost.exe c:TestTemplate.tt (中间包含一个空格),然后按下Enter (回车)键。
此时在C盘根目录中就已经生成了 TestTemplate1.txt,内容为:
Text Template Host Test This is a test This is a test This is a test