在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基于 ...
随机推荐
- 搞IT的技术人员为什么会如此苦逼
http://www.cnblogs.com/springmvc-hibernate/archive/2012/05/10/2493733.html ————————————————————————— ...
- 切比雪夫多项式(Chebyshev Polynomials)
切比雪夫多项式在逼近理论中有重要的应用.这是因为第一类切比雪夫多项式的根(被称为切比雪夫节点)可以用于多项式插值.相应的插值多项式能最大限度地降低龙格现象,并且提供多项式在连续函数的最佳一致逼近. 参 ...
- jQuery 尺寸 方法
jQuery 提供多个处理尺寸的重要方法: width() height() innerWidth() innerHeight() outerWidth() outerHeight()
- libsvm easy.py ValueError: need more than 0 values to unpack windows下终极解决
现象是: python easy.py train test 输出: Scaling training data...WARNING: original #nonzeros 100389 new #n ...
- Unity官方发布热更新方案性能对照
孙广东 2016.3.11 Unity应用的iOS热更新 作者:丁治宇 Unity TechnologiesChina Agenda • 什么是热更新 • 为何要热更新 • 怎样在iOS 上对 ...
- Python中的多进程与多线程/分布式该如何使用
在批评Python的讨论中,常常说起Python多线程是多么的难用.还有人对 global interpreter lock(也被亲切的称为“GIL”)指指点点,说它阻碍了Python的多线程程序同时 ...
- Laravel5.1 模型 --软删除
软删除是比较实用的一种删除手段,比如说 你有一本账 有一笔记录你觉得不对给删了 过了几天发现不应该删除,这时候软删除的目的就实现了 你可以找到已经被删除的数据进行操作 可以是还原也可以是真正的删除. ...
- Ubuntu 下Apache安装和配置2
在Ubuntu上安装Apache,有两种方式:1 使用开发包的打包服务,例如使用apt-get命令:2 从源码构建Apache.本文章将详细描述这两种不同的安装方式. 方法一:使用开发包的打包服务—— ...
- cocos2d-x学习日志(18) --程序是怎样開始执行与结束?
问题的由来 怎么样使用 Cocos2d-x 高速开发游戏.方法非常easy,你能够看看其自带的例程,或者从网上搜索教程,执行起第一个HelloWorld,然后在 HelloWorld 里面写相关逻辑代 ...
- selenium的元素定位-鼠标事件
鼠标事件 ActionChains 类提供了鼠标操作的常用方法: perform(): 执行所有 ActionChains 中存储的行为: context_click(): 右击: double_cl ...