从数据到代码——通过代码生成机制实现强类型编程[下篇]-阿里云开发者社区

开发者社区> 行者武松> 正文

从数据到代码——通过代码生成机制实现强类型编程[下篇]

简介:
+关注继续查看

《上篇》中,我们实现了将保存有消息条目的XML向CodeDOM的转换,即是将XML文件生成一个CodeCompileUnit对象,而该CodeCompileUnit对象反映出来的DOM层次和我们将会生成的代码文件向匹配。在下篇中,我们将实现整个代码生成系统的第二个步骤——通过VS的Custom Tool实现数据(保存消息条目的XML)向代码文件的自动转换

一、让MessageCodeGenerator继承BaseCodeGeneratorWithSite

《上篇》我们创建了MessageCodeGenerator类,定义了如下一个BuildCodeObject方法实现将一个XmlDocument转换成一个CodeCompileUnit对象。

   1: namespace Artech.CodeDomGenerator
   2: {
   3:     public class MessageCodeGenerator
   4:     {     
   5:          // Others...
   6:         public CodeCompileUnit BuildCodeObject(XmlDocument messages);
   7:     }
   8: }

现在我们需要做的是让这个MessageCodeGenerator继承一个特殊的类:BaseCodeGeneratorWithSiteBaseCodeGeneratorWithSite所在的程序集名称为Microsoft.VisualStudio.TextTemplating.VSHost.10.0.dll,这是一个Visual Studio SDK的程序集。我们例子采用的是Visual Studio 2010,你可以在如下的目录中找到该程序集:%ProgramFiles%Microsoft Visual Studio 2010 SDK\VisualStudioIntegration\Common\Assemblies\v4.0。如果你没有安装VS 2010 SDK,你可以从这里下载。

除了添加对Microsoft.VisualStudio.TextTemplating.VSHost.10.0.dll程序集的引用外,你还需要添加两个额外的程序集引用:Microsoft.VisualStudio.OLE.Interop.dllMicrosoft.VisualStudio.Shell.Interop.dll,它们所在的目录分别是%ProgramFiles%Microsoft Visual Studio 2010 SDK\VisualStudioIntegration\Common\Assemblies\v4.0%ProgramFiles%Microsoft Visual Studio 2010 SDK\VisualStudioIntegration\Common\Assemblies\v2.0

添加了相应的程序集引用,并将BaseCodeGeneratorWithSite这个抽象类作为MessageCodeGenerator的基类后,需要实现如下两个抽象方法:GenerateCodeGetDefaultExtension

   1: namespace Artech.CodeDomGenerator
   2: {   
   3:     public class MessageCodeGenerator : BaseCodeGeneratorWithSite
   4:     {      
   5:         public CodeCompileUnit BuildCodeObject(XmlDocument messages)
   6:         {
   7:             //......
   8:         }              
   9:         protected override byte[] GenerateCode(string inputFileName, string inputFileContent)
  10:         {
  11:            var messageDoc = new XmlDocument();
  12:             messageDoc.LoadXml(inputFileContent);
  13:             var codeObject = BuildCodeObject(messageDoc);
  14:             CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
  15:             CodeGeneratorOptions options = new CodeGeneratorOptions();
  16:             options.BracingStyle = "C";
  17:             using (StringWriter writer = new StringWriter())
  18:             {               
  19:                 provider.GenerateCodeFromCompileUnit(codeObject, writer, options);
  20:                 string code = writer.ToString();
  21:                 byte[] preambleBytes = Encoding.Unicode.GetPreamble();
  22:                 byte[] codeBytes = Encoding.Unicode.GetBytes(code);
  23:                 byte[] result = new byte[preambleBytes.Length + codeBytes.Length];
  24:                 Buffer.BlockCopy(preambleBytes, 0, result, 0, preambleBytes.Length);
  25:                 Buffer.BlockCopy(codeBytes, 0, result, preambleBytes.Length, codeBytes.Length);
  26:                 return result; 
  27:             }
  28:         }
  29:  
  30:         public override string GetDefaultExtension()
  31:         {
  32:             return ".cs";
  33:         }
  34:     }
  35: }

GenerateCode返回的字节数组表示最终生成的的代码的内容,在这里的逻辑很简单,就是通过CodeDomProviderCodeCompileUnit转化成基于具体编程语言(在这里我们只考虑C#)的代码。而GetDefaultExtension返回生成的代码文件的扩展名,在这里自然是“.cs”。

二、将MessageCodeGenerator注册成COM组件

到目前我们MessageCodeGenerator完全通过托管程序编写,但是VS和扩展是通过COM的方式进行交互的,所以我们需要将MessageCodeGenerator注册成COM组件。我们首先需要做的是对MessageCodeGenerator所在的程序集进行注册。一般地,进行注册的程序集都具有一个强名称,所以我们先对程序集进行签名。这只需要对定义MessageCodeGenerator所在的项目的“签名”选项进行如下设置就可以了。

image我们还需要对程序集的COM可见性进行相应的设置。对于COM可见性的设置,我们只需在AssemblyInfo.cs文件中,添加如下一个ComVisibleAttribute特性并将参数设置成true即可(默认为false)。

   1: // Setting ComVisible to false makes the types in this assembly not visible 
   2: // to COM components.  If you need to access a type in this assembly from 
   3: // COM, set the ComVisible attribute to true on that type.
   4: [assembly: ComVisible(true)]

为了让我们定义的MessageCodeGenerator通过COM组件的形式暴露出来,我们需要功过在器类型上通过应用一个GuidAttribute指定一个唯一标识。这个唯一标识可以通过VS自带的GUID生成器生成。

   1: [Guid("F9A0FCB3-864F-4B87-885B-FAEBC860BD64")]
   2: public class MessageCodeGenerator : BaseCodeGeneratorWithSite
   3: {  
   4:     //Others...
   5: }

程序集的注册通过命令行工具RegAsm.exe完成,我们只需要启动通过VS 2010的命名行工具,执行RegAsm.exe命令对编译生成的程序集进行注册。

   1: RegAsm "c:\CodeDOMGenerator\Artech.CodeDomGenerator.CodeGenerator.dll"

实际上,我们也可以直接通过VS对相应的项目进行相应的设置,让VS在编译完成后自动完成对目标程序基的注册。你只需要在项目设置对话框中的Build页,钩选“Register for COM interop”即可。

image

注:由于我们的MessageCodeGenerator内部引用到了另一个程序集Microsoft.VisualStudio.Shell.Interop.dll中的某些类型,你需要通过执行如下RegAsm.exe命令行对该程序基进行注册,并采用/tlb开关生成类型库。

   1: RegAsm /tlb "%ProgramFiles%Microsoft Visual Studio 2010 SDK\VisualStudioIntegration\Common\Assemblies\v2.0\Microsoft.VisualStudio.Shell.Interop.dll"

三、设置注册表

到目前为止,我们定义的代码生成器MessageCodeGenerator已经通过COM组件的形式暴露出来了,我们需要作的就是让VS能够正常地加载该COM组件,这通过设置VS相关的注册表信息来完成。VS2010与代码生成相关的注册表项定义在HKLM\Software\Microsoft\VisualStudio\10.0\Generators\节点下。该节点下的子节点(Key)均通过相应的GUID表示,不同的GUID实际上表示的是相应的编程语言。其中{164B10B9-B200-11D0-8C61-00A0C91E29D5}代表VB.NET,而C#对应的GUID为下图选中的{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}

image 现在我们需要在表示C#的节点下创建一个Key,并起名为MessageCodeGenerator,即我们约定的代码生成器的名称。

image 

如上图所示,我们需要对我们添加的注册表键进行如下三项设置:

  • (Default)[REG_SZ]:设置代码生成器的表述性信息;
  • CLSID[REG_SZ]:作为COM组件的代码生成器的GUID,即我们在定义MessageCodeGenerator类新通过GuidAttribute特性指定的GUID,注意不要忘了花括号;
  • GeneratesDesignTimeSource[REG_WWORD]: 0或者1,表明是否提供设计时原代码生成的支持

四、通过Custom Tool直接通过XML生成C#代码

现在我们就可以来直接使用我们我们的MessageCodeGenerator了。现在我们创建一个项目,添加一个用于保存消息的XML文件,比如起名为Messages.xml,内容如下:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <messages>
   3:   <message id="MandatoryField" value="The {0} is mandatory."  category="Validation"/>
   4:   <message id="GreaterThan" value="The {0} must be greater than {1}."  category="Validation"/>
   5:   <message id="ReallyDelete" value="Do you really want to delete the {0}."  category="Confirmation"/>
   6: </messages>

然后右击该XML文件,在弹出的上下文菜单中选择Properties选项。你会发现在属性对话框中有个叫作Custom Tool的属性名称,在该项上填写上我们的代码生成器的名称:MessageCodeGenerator。

image 此后,当你右击该XML文件时,在上下文菜单中都会多出一个叫做Run Custom Tool的项目,选择它我们的.cs文件将会自动生成,

image

该.cs文件和我们在《上篇》给出的代码一模一样。那么我们就可以借助于生成出来的代码,以一种强类型的方式获取相应的、被格式化的消息文本

   1: //------------------------------------------------------------------------------
   2: // <auto-generated>
   3: //     This code was generated by a tool.
   4: //     Runtime Version:4.0.30319.1
   5: //
   6: //     Changes to this file may cause incorrect behavior and will be lost if
   7: //     the code is regenerated.
   8: // </auto-generated>
   9: //------------------------------------------------------------------------------
  10:  
  11: namespace Artech.CodeDomGenerator
  12: {
  13:     
  14:     
  15:     public class Messages
  16:     {
  17:         
  18:         public class Validation
  19:         {
  20:             
  21:             public static Artech.CodeDomGenerator.MessageEntry MandatoryField = new Artech.CodeDomGenerator.MessageEntry("MandatoryField", "The {0} is mandatory.", "Validation");
  22:             
  23:             public static Artech.CodeDomGenerator.MessageEntry GreaterThan = new Artech.CodeDomGenerator.MessageEntry("GreaterThan", "The {0} must be greater than {1}.", "Validation");
  24:         }
  25:         
  26:         public class Confirmation
  27:         {
  28:             
  29:             public static Artech.CodeDomGenerator.MessageEntry ReallyDelete = new Artech.CodeDomGenerator.MessageEntry("ReallyDelete", "Do you really want to delete the {0}.", "Confirmation");
  30:         }
  31:     }
  32: }

五、将MessageCodeGenerator和文件扩展名绑定

实际上我们可以看出VS代码生成机制的本质:将一个文件作为源文件(Source),利用相应的生成器生成目标文件(Destination)。至于采用怎样的生成器,则是通过源文件的Custom Tool属性进行匹配的。除了这种需要手工设置文件属性的方式进行源文件和生成器之间的匹配关系外,还具有另一种更为方便的匹配方式:基于源文件扩展名的匹配

现在我们的消息文件时通过一个XML文件(文件的结构和扩展名均是XML),如果我们现在给它一种特殊的扩展名,并且将设置源文件扩展名和代码生成器的匹配关系,就无需再手工地为源文件设置Custom Tool这一属性了

实际上,我们可以一个简单的注册表设置就可以实现这样的功能。假设作为MessageCodeGenerator的源文件的扩展名为msg(不要认为是OutLook邮件消息),我们住需要在上面提到过的基于某种编程语言的注册表节点下,创建一个以扩展名命名的Key,并将Default值直接设置成代码生成器的名称即可。image

现在当你添加一个扩展名为.msg的文件后,Custom Tool自动为你设置成MessageCodeGenerator。无需手工设置,你就可以直接通过Run Custom Tool生成相应的代码文件了。

image

 

从数据到代码——通过代码生成机制实现强类型编程[上篇]
从数据到代码——通过代码生成机制实现强类型编程[下篇]
从数据到代码——基于T4的代码生成方式
创建代码生成器可以很简单:如何通过T4模板生成代码?[上篇]
创建代码生成器可以很简单:如何通过T4模板生成代码?[下篇] 

 


作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Cocos2d-x 3.2编译生成Android程序出错的解决方案
      最近升级到Cocos2d-x 3.2正式版,iOS程序编译没任何问题,结果使用cocos compile -p android编译生成APK程序,结果悲剧了,出现以下错误。
1014 0
MyBatis Generator 代码生成器 快速入门指南
要使用MyBatis Generator(MBG)快速启动并运行,请按照下列步骤操作:1、适当地创建并填写配置文件。至少必须指定:一个元素来指定如何连接到目标数据库一个元素来指定生成的Java模型对象的目标包和目标项目用于指定生成的SQL映射文件的目标包和目标项目的元素(可选)一个元素来指定生成的客户端接口和类的目标包和目标项目(如果不希望生成Java客户端代码,则可以省略元素)至少有一个数据库元素有关配置文件的示例,请参阅XML配置文件参考页面。
804 0
MyBatis Generator (MBG) 代码生成器简介
MyBatis Generator(MBG)是MyBatis MyBatis和iBATIS的代码生成器。它将生成所有版本的MyBatis的代码,以及版本2.2.0之后的iBATIS版本。它将内省数据库表(或许多表),并将生成可用于访问表的工件。
1330 0
代码生成框架Velocity
代码生成框架VelocityVelocity是一个基于Java的模板引擎,用户可以使用模板语言VTL来引用由Java代码定义的对象。
1012 0
EnjoyCSS – 在线的,先进的 CSS3 代码生成器
  EnjoyCSS 是一款先进的 CSS3 代码生成工具,可以让你摆脱日常的编码。它方便和易于使用的用户界面允许您快速,无需编码就可以调节出丰富的图形样式。您将能够玩转所有的 EnjoyCSS 参数,就像在 Photoshop 或 illustarator 中一样,结合所有可能的 CSS3 样式功能。
1041 0
05.Java网络编程(代码实践)
计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路链接起来,在网络操作系统,网络管理软件及网络通信协议的协调下,实现资源贡献和信息传递的计算机系统 网络编程就是用来实现网络互连的不同计算机上运行的程序间可以进行数据交换 网...
783 0
RandomUser – 生成随机用户 JSON 数据的 API
  RandomUser 是一个 API,它为您提供了一个或者一批随机生成的用户。这些用户可以在 Web 应用程序原型中用作占位符,将节省您创建自己的占位符信息的时间。您可以使用 AJAX 或其他方法来调用 RandomUser 获取随机用户数据。
989 0
Spring Boot项目利用MyBatis Generator进行数据层代码自动生成
概 述 MyBatis Generator (简称 MBG) 是一个用于 MyBatis和 iBATIS的代码生成器。它可以为 MyBatis的所有版本以及 2.2.0之后的 iBATIS版本自动生成 ORM层代码,典型地包括我们日常需要手写的 POJO、mapper xml 以及 mapper 接口等。
1838 0
+关注
行者武松
杀人者,打虎武松也。
17142
文章
2569
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载