zoukankan      html  css  js  c++  java
  • 在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 文件中的代码:

    1. using System;
    2. using System.IO;
    3. using System.CodeDom.Compiler;
    4. using System.Collections.Generic;
    5. using System.Text;
    6. using Microsoft.VisualStudio.TextTemplating;
    7.  
    8. namespace CustomHost
    9. {
    10. //The text template transformation engine is responsible for running
    11. //the transformation process.
    12. //The host is responsible for all input and output, locating files,
    13. //and anything else related to the external environment.
    14. //-------------------------------------------------------------------------
    15. class CustomCmdLineHost : ITextTemplatingEngineHost
    16. {
    17. //the path and file name of the text template that is being processed
    18. //---------------------------------------------------------------------
    19. internal string TemplateFileValue;
    20. public string TemplateFile
    21. {
    22. get { return TemplateFileValue; }
    23. }
    24. //This will be the extension of the generated text output file.
    25. //The host can provide a default by setting the value of the field here.
    26. //The engine can change this value based on the optional output directive
    27. //if the user specifies it in the text template.
    28. //---------------------------------------------------------------------
    29. private string fileExtensionValue = ".txt";
    30. public string FileExtension
    31. {
    32. get { return fileExtensionValue; }
    33. }
    34. //This will be the encoding of the generated text output file.
    35. //The host can provide a default by setting the value of the field here.
    36. //The engine can change this value based on the optional output directive
    37. //if the user specifies it in the text template.
    38. //---------------------------------------------------------------------
    39. private Encoding fileEncodingValue = Encoding.UTF8;
    40. public Encoding FileEncoding
    41. {
    42. get { return fileEncodingValue; }
    43. }
    44. //These are the errors that occur when the engine processes a template.
    45. //The engine passes the errors to the host when it is done processing,
    46. //and the host can decide how to display them. For example, the host
    47. //can display the errors in the UI or write them to a file.
    48. //---------------------------------------------------------------------
    49. private CompilerErrorCollection errorsValue;
    50. public CompilerErrorCollection Errors
    51. {
    52. get { return errorsValue; }
    53. }
    54. //The host can provide standard assembly references.
    55. //The engine will use these references when compiling and
    56. //executing the generated transformation class.
    57. //--------------------------------------------------------------
    58. public IList<string> StandardAssemblyReferences
    59. {
    60. get
    61. {
    62. return new string[]
    63. {
    64. //If this host searches standard paths and the GAC,
    65. //we can specify the assembly name like this.
    66. //---------------------------------------------------------
    67. //"System"
    68.  
    69. //Because this host only resolves assemblies from the
    70. //fully qualified path and name of the assembly,
    71. //this is a quick way to get the code to give us the
    72. //fully qualified path and name of the System assembly.
    73. //---------------------------------------------------------
    74. typeof(System.Uri).Assembly.Location
    75. };
    76. }
    77. }
    78. //The host can provide standard imports or using statements.
    79. //The engine will add these statements to the generated
    80. //transformation class.
    81. //--------------------------------------------------------------
    82. public IList<string> StandardImports
    83. {
    84. get
    85. {
    86. return new string[]
    87. {
    88. "System"
    89. };
    90. }
    91. }
    92. //The engine calls this method based on the optional include directive
    93. //if the user has specified it in the text template.
    94. //This method can be called 0, 1, or more times.
    95. //---------------------------------------------------------------------
    96. //The included text is returned in the context parameter.
    97. //If the host searches the registry for the location of include files,
    98. //or if the host searches multiple locations by default, the host can
    99. //return the final path of the include file in the location parameter.
    100. //---------------------------------------------------------------------
    101. public bool LoadIncludeText(string requestFileName, out string content, out string location)
    102. {
    103. content = System.String.Empty;
    104. location = System.String.Empty;
    105. //If the argument is the fully qualified path of an existing file,
    106. //then we are done.
    107. //----------------------------------------------------------------
    108. if (File.Exists(requestFileName))
    109. {
    110. content = File.ReadAllText(requestFileName);
    111. return true;
    112. }
    113. //This can be customized to search specific paths for the file.
    114. //This can be customized to accept paths to search as command line
    115. //arguments.
    116. //----------------------------------------------------------------
    117. else
    118. {
    119. return false;
    120. }
    121. }
    122. //Called by the Engine to enquire about
    123. //the processing options you require.
    124. //If you recognize that option, return an
    125. //appropriate value.
    126. //Otherwise, pass back NULL.
    127. //--------------------------------------------------------------------
    128. public object GetHostOption(string optionName)
    129. {
    130. object returnObject;
    131. switch (optionName)
    132. {
    133. case "CacheAssemblies":
    134. returnObject = true;
    135. break;
    136. default:
    137. returnObject = null;
    138. break;
    139. }
    140. return returnObject;
    141. }
    142. //The engine calls this method to resolve assembly references used in
    143. //the generated transformation class project and for the optional
    144. //assembly directive if the user has specified it in the text template.
    145. //This method can be called 0, 1, or more times.
    146. //---------------------------------------------------------------------
    147. public string ResolveAssemblyReference(string assemblyReference)
    148. {
    149. //If the argument is the fully qualified path of an existing file,
    150. //then we are done. (This does not do any work.)
    151. //----------------------------------------------------------------
    152. if (File.Exists(assemblyReference))
    153. {
    154. return assemblyReference;
    155. }
    156. //Maybe the assembly is in the same folder as the text template that
    157. //called the directive.
    158. //----------------------------------------------------------------
    159. string candidate = Path.Combine(Path.GetDirectoryName(this.TemplateFile), assemblyReference);
    160. if (File.Exists(candidate))
    161. {
    162. return candidate;
    163. }
    164. //This can be customized to search specific paths for the file
    165. //or to search the GAC.
    166. //----------------------------------------------------------------
    167. //This can be customized to accept paths to search as command line
    168. //arguments.
    169. //----------------------------------------------------------------
    170. //If we cannot do better, return the original file name.
    171. return "";
    172. }
    173. //The engine calls this method based on the directives the user has
    174. //specified in the text template.
    175. //This method can be called 0, 1, or more times.
    176. //---------------------------------------------------------------------
    177. public Type ResolveDirectiveProcessor(string processorName)
    178. {
    179. //This host will not resolve any specific processors.
    180. //Check the processor name, and if it is the name of a processor the
    181. //host wants to support, return the type of the processor.
    182. //---------------------------------------------------------------------
    183. if (string.Compare(processorName, "XYZ", StringComparison.OrdinalIgnoreCase) == 0)
    184. {
    185. //return typeof();
    186. }
    187. //This can be customized to search specific paths for the file
    188. //or to search the GAC
    189. //If the directive processor cannot be found, throw an error.
    190. throw new Exception("Directive Processor not found");
    191. }
    192. //A directive processor can call this method if a file name does not
    193. //have a path.
    194. //The host can attempt to provide path information by searching
    195. //specific paths for the file and returning the file and path if found.
    196. //This method can be called 0, 1, or more times.
    197. //---------------------------------------------------------------------
    198. public string ResolvePath(string fileName)
    199. {
    200. if (fileName == null)
    201. {
    202. throw new ArgumentNullException("the file name cannot be null");
    203. }
    204. //If the argument is the fully qualified path of an existing file,
    205. //then we are done
    206. //----------------------------------------------------------------
    207. if (File.Exists(fileName))
    208. {
    209. return fileName;
    210. }
    211. //Maybe the file is in the same folder as the text template that
    212. //called the directive.
    213. //----------------------------------------------------------------
    214. string candidate = Path.Combine(Path.GetDirectoryName(this.TemplateFile), fileName);
    215. if (File.Exists(candidate))
    216. {
    217. return candidate;
    218. }
    219. //Look more places.
    220. //----------------------------------------------------------------
    221. //More code can go here...
    222. //If we cannot do better, return the original file name.
    223. return fileName;
    224. }
    225. //If a call to a directive in a text template does not provide a value
    226. //for a required parameter, the directive processor can try to get it
    227. //from the host by calling this method.
    228. //This method can be called 0, 1, or more times.
    229. //---------------------------------------------------------------------
    230. public string ResolveParameterValue(string directiveId, string processorName, string parameterName)
    231. {
    232. if (directiveId == null)
    233. {
    234. throw new ArgumentNullException("the directiveId cannot be null");
    235. }
    236. if (processorName == null)
    237. {
    238. throw new ArgumentNullException("the processorName cannot be null");
    239. }
    240. if (parameterName == null)
    241. {
    242. throw new ArgumentNullException("the parameterName cannot be null");
    243. }
    244. //Code to provide "hard-coded" parameter values goes here.
    245. //This code depends on the directive processors this host will interact with.
    246. //If we cannot do better, return the empty string.
    247. return String.Empty;
    248. }
    249. //The engine calls this method to change the extension of the
    250. //generated text output file based on the optional output directive
    251. //if the user specifies it in the text template.
    252. //---------------------------------------------------------------------
    253. public void SetFileExtension(string extension)
    254. {
    255. //The parameter extension has a '.' in front of it already.
    256. //--------------------------------------------------------
    257. fileExtensionValue = extension;
    258. }
    259. //The engine calls this method to change the encoding of the
    260. //generated text output file based on the optional output directive
    261. //if the user specifies it in the text template.
    262. //----------------------------------------------------------------------
    263. public void SetOutputEncoding(System.Text.Encoding encoding, bool fromOutputDirective)
    264. {
    265. fileEncodingValue = encoding;
    266. }
    267. //The engine calls this method when it is done processing a text
    268. //template to pass any errors that occurred to the host.
    269. //The host can decide how to display them.
    270. //---------------------------------------------------------------------
    271. public void LogErrors(CompilerErrorCollection errors)
    272. {
    273. errorsValue = errors;
    274. }
    275. //This is the application domain that is used to compile and run
    276. //the generated transformation class to create the generated text output.
    277. //----------------------------------------------------------------------
    278. public AppDomain ProvideTemplatingAppDomain(string content)
    279. {
    280. //This host will provide a new application domain each time the
    281. //engine processes a text template.
    282. //-------------------------------------------------------------
    283. return AppDomain.CreateDomain("Generation App Domain");
    284. //This could be changed to return the current appdomain, but new
    285. //assemblies are loaded into this AppDomain on a regular basis.
    286. //If the AppDomain lasts too long, it will grow indefintely,
    287. //which might be regarded as a leak.
    288. //This could be customized to cache the application domain for
    289. //a certain number of text template generations (for example, 10).
    290. //This could be customized based on the contents of the text
    291. //template, which are provided as a parameter for that purpose.
    292. }
    293. }
    294. //This will accept the path of a text template as an argument.
    295. //It will create an instance of the custom host and an instance of the
    296. //text templating transformation engine, and will transform the
    297. //template to create the generated text output file.
    298. //-------------------------------------------------------------------------
    299. class Program
    300. {
    301. static void Main(string[] args)
    302. {
    303. try
    304. {
    305. ProcessTemplate(args);
    306. }
    307. catch (Exception ex)
    308. {
    309. Console.WriteLine(ex.Message);
    310. }
    311. }
    312. static void ProcessTemplate(string[] args)
    313. {
    314. string templateFileName = null;
    315. if (args.Length == 0)
    316. {
    317. throw new System.Exception("you must provide a text template file path");
    318. }
    319. templateFileName = args[0];
    320. if (templateFileName == null)
    321. {
    322. throw new ArgumentNullException("the file name cannot be null");
    323. }
    324. if (!File.Exists(templateFileName))
    325. {
    326. throw new FileNotFoundException("the file cannot be found");
    327. }
    328. CustomCmdLineHost host = new CustomCmdLineHost();
    329. Engine engine = new Engine();
    330. host.TemplateFileValue = templateFileName;
    331. //Read the text template.
    332. string input = File.ReadAllText(templateFileName);
    333. //Transform the text template.
    334. string output = engine.ProcessTemplate(input, host);
    335. string outputFileName = Path.GetFileNameWithoutExtension(templateFileName);
    336. outputFileName = Path.Combine(Path.GetDirectoryName(templateFileName), outputFileName);
    337. outputFileName = outputFileName + "1" + host.FileExtension;
    338. File.WriteAllText(outputFileName, output, host.FileEncoding);
    339.  
    340. foreach (CompilerError error in host.Errors)
    341. {
    342. Console.WriteLine(error.ToString());
    343. }
    344. }
    345. }
    346. }

    5. 仅对于 Visual Basic,打开“项目”菜单,单击“CustomHost 属性”。 在“启动对象”列表中单击“CustomHost.Program”。

    6. 在“文件”菜单上,单击“全部保存”。

    7. 在“生成”菜单上,单击“生成解决方案”。

    进行编译并测试

    若要测试自定义宿主,您需要编写一个文本模板,然后运行自定义宿主,将文本模板的名称传递给它并验证模板转换。

    创建文本模板测试自定义宿主

    1. 在C盘根目录中创建一个文本文件,将其命名为 TestTemplate.tt 。

    可以使用任何文本编辑器(例如记事本)来创建文件。

    2. 将以下内容添加到文件中:

    1. Text Template Host Test
    2.  
    3. <#@ template debug="true" #>
    4.  
    5. <# //Uncomment this line to test that the host allows the engine to set the extension. #>
    6. <# //@ output extension=".htm" #>
    7.  
    8. <# //Uncomment this line if you want to debug the generated transformation class. #>
    9. <# //System.Diagnostics.Debugger.Break(); #>
    10.  
    11. <# for (int i=0; i<3; i++)
    12. {
    13. WriteLine("This is a test");
    14. }
    15. #>

    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
     
     
  • 相关阅读:
    Vue 中样式穿透 /deep/
    Vue 数据冻结 Object.freeze
    Vue 启动项目内存溢出
    Typora[ markdown ] 使用3之----- 语法高亮显示
    Typora[ markdown ] 使用2之-----空格显示
    手动创建mysql数据库的语句记录
    api不能自动注入条件的解决方法
    【WTM框架】查询列表显示正常,但是导出的时候查询条件不起作用的问题记录及解决方法
    WTM问题之“数据列表”控件出现横向的滚动条的解决方法
    树莓派docker无法限制内存Your kernel does not support memory limit capabilities or the cgroup is not mounted
  • 原文地址:https://www.cnblogs.com/chencidi/p/6426715.html
Copyright © 2011-2022 走看看