在C#中动态编译T4模板代码
转: 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
在C#中动态编译T4模板代码的更多相关文章
- [Unity] 编辑器运行中动态编译执行C#代码
(一)问题 之前写Lua时,修改完代码 reload 就可以热重载代码,调试起来十分方便(重构则十分痛苦). 现在使用 C# 做开发,目前还没找到比较方便地进行热重载的方式.只能退而求其次,在调试上找 ...
- 转: angularjs 指令中动态编译的方法(适用于有异步请求的情况) 内嵌指令无效
angular的坑很多 例子: 在directive的link中有一个$http请求,当请求完成后根据返回的值动态做element.append('......');这个操作, 能显示没问题,可问题是 ...
- C#动态编译、执行代码
在开始之前,先熟悉几个类及部分属性.方法:CSharpCodeProvider.ICodeCompiler.CompilerParameters.CompilerResults.Assembly. 一 ...
- 如何用C#动态编译、执行代码
在开始之前,先熟悉几个类及部分属性.方法:CSharpCodeProvider.ICodeCompiler.CompilerParameters.CompilerResults.Assembly. 一 ...
- 真实项目中VS2015中自建T4模板生成文件的使用
有可能许多小伙伴们发现,vs2015和2012的自带T4模板中的.tt文件改变非常之多,如果仅仅copyEF系统自己生成的模板文件,那可累了.以下是我自己整理的在2012和2015中都可以试用的代码. ...
- [转]如何用C#动态编译、执行代码
在开始之前,先熟悉几个类及部分属性.方法:CSharpCodeProvider.ICodeCompiler.CompilerParameters.CompilerResults.Assembly. 一 ...
- 如何用C#动态编译、执行代码[转]
原文链接 在开始之前,先熟悉几个类及部分属性.方法:CSharpCodeProvider.ICodeCompiler.CompilerParameters.CompilerResults.Assemb ...
- Python中动态编译函数compile(source, filename, mode, ......)参数filename的作用是什么?
动态编译函数compile调用语法如下: compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1) 其中的fi ...
- 使用PyQt(Python+Qt)+动态编译36行代码实现的计算器
PyQt是基于跨平台的图形界面C++开发工具Qt加Python包装的一个GPL软件(GPL是GNU General Public License的缩写,是GNU通用公共授权非正式的中文翻译),Qt基于 ...
随机推荐
- python 中property函数如何实现
实际上,在python中property(fget,fset,fdel,doc)函数不是一个真正的函数,他其实是拥有很多特殊方法的类. 这特殊类总的很多方法完成了property函数中的所有工作,涉及 ...
- eclipse: Program "g++" not found in PATH
gdb命令行调试虽然还行,但是确实不如图形界面的直观...个人还是不习惯,就开始鼓捣eclipse的c/c++IDE(VS2013安装需要IE10,蛋疼,懒得弄) Build一个C工程的时候报错.我安 ...
- Unity学习笔记 - UI System(一)
转载请注明出处: EnigmaJJ http://www.cnblogs.com/twjcnblog/p/5850648.html 术语 Canvas是Unity的原生组件,Unity的渲染系统使用C ...
- java继承机制
1 继承 关键字:extends java没有多重继承 实例 父类: package unit4; public class Base { public int publicVarofBase= ...
- Java对象类型的判断
instanceof 判断某个对象是否是某个类的实例或者某个类的子类的实例.它的判断方式大概是这样的: public<T> boolean function(Object obj, Cla ...
- configure: error : no acceptable C compiler found in $PATH
先要用yum install yum-fastestmirror更新下源 # yum -y install gcc
- Java并发编程(九)安全发布
之前讨论是如何将对象封闭在线程之中,这样可以减少一些并发带来的同步和可见性问题.但是在有些时候,我们希望在多个线程间共享对象,此时必须确保安全地进行共享. [不安全发布的示例] 可见性问题:其他线程看 ...
- JAVA自定义注解 ------ Annotation
日常开发工作中,合理的使用注解,可以简化代码编写以及使代码结构更加简单,下面记录下,JAVA自定义注解的开发过程. 定义注解声明类. 编写注解处理器(主要起作用部分). 使用注解. 相关知识点介绍, ...
- linux用户密码生成
linux账户保存在/etc/passwd,密码保存在/etc/shadow. 通过man 5 passwd,man 5 shadow可查看文件中各字段含义. encrypted password ...
- 【ask】vmware(NAT)中的linux突然无法访问互联网网址,但是直接用ip可以访问。
前两天虚拟机里的linuxmint不知何故,突然无法访问互联网了.依稀记得是升级了win7下面的360安全卫士之后发生的事情.所以, 第1步就开始去找防火墙的各种设置,结果没有查到结果. 第2步猛然看 ...