2019独角兽企业重金招聘Python工程师标准>>>

之前写了一些关于代码生成的文章,提供了两种不同方式的代码生成解决方案,即CodeDOM+Custom Tool和T4。对于ASP.NET应用,你还有第三种选择——自定义BuildProvider。[文中涉及的源代码从这里下载]

目录      
一、BuildProvider是什么?      
二、将XML表示的消息转换成VB.NET或者C#代码      
三、将XML转换成CodeDOM      
四、自定义BuildProvider      
五、BuildProvider的应用

一、BuildProvider是什么?

对 于ASP.NET应用的开发者来说,你可能不知道什么是BuildProvider,但是你几乎无时无刻不在使用它所带来的代码生成机制。当你创建一 个.aspx文件的时候,为什么会自动创建对应源代码?当你在该.aspx页面中以XML的方式添加一个按钮,源代码中为什么会自动添加一个同名的属性。 实际上,ASP.NET就是通过一个特殊的BuildProvider实现了将.aspx文件内容转换成相应的源代码,这个特殊的.aspx文件就是:PageBuildProvider。基于不同的文件类型,ASP.NET会采用不同的BuildProvider进行源代码的生成。比如UserControlBuildProvider和MasterPageBuildProvider分 别实现了基于用户控件文件(.ascx)和母板页(.master)的源代码生成。你可以通过查看%Windows%\Microsoft.NET \Framework\v2.0.50727\CONFIG\web.config看看在默认情况下使用的BuildProvider以及它基于的源文件 类型(扩展名)。

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <configuration>
   3:   <system.web>
   4:     ... ...
   5:     <compilation>
   6:       <buildProviders>
   7:         <add extension=".aspx" type="System.Web.Compilation.PageBuildProvider"/>
   8:         <add extension=".ascx" type="System.Web.Compilation.UserControlBuildProvider"/>
   9:         <add extension=".master" type="System.Web.Compilation.MasterPageBuildProvider"/>
  10:         <add extension=".asmx" type="System.Web.Compilation.WebServiceBuildProvider"/>
  11:         <add extension=".ashx" type="System.Web.Compilation.WebHandlerBuildProvider"/>
  12:         <add extension=".soap" type="System.Web.Compilation.WebServiceBuildProvider"/>
  13:         <add extension=".resx" type="System.Web.Compilation.ResXBuildProvider"/>
  14:         <add extension=".resources" type="System.Web.Compilation.ResourcesBuildProvider"/>
  15:         <add extension=".wsdl" type="System.Web.Compilation.WsdlBuildProvider"/>
  16:         <add extension=".xsd" type="System.Web.Compilation.XsdBuildProvider"/>
  17:         <add extension=".js" type="System.Web.Compilation.ForceCopyBuildProvider"/>
  18:         <add extension=".lic" type="System.Web.Compilation.IgnoreFileBuildProvider"/>
  19:         <add extension=".licx" type="System.Web.Compilation.IgnoreFileBuildProvider"/>
  20:         <add extension=".exclude" type="System.Web.Compilation.IgnoreFileBuildProvider"/>
  21:         <add extension=".refresh" type="System.Web.Compilation.IgnoreFileBuildProvider"/>
  22:         <add extension=".svc" type="System.ServiceModel.Activation.ServiceBuildProvider, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
  23:         <add extension=".xoml" type="System.ServiceModel.Activation.WorkflowServiceBuildProvider, System.WorkflowServices, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
  24:       </buildProviders>
  25:     </compilation>
  26:   </system.web>
  27: </configuration>

既然ASP.NET可以通过相应的BuildProvider为不同类型的文件生成相应的源代码,我们自然也能自定义BuildProvider实 现我们希望的代码生成机制。为了让读者和之前提供的两种方式的代码生成机制作一个对于,我们依然采用相同的应用场景:将以XML表示的数据转换成代码,以 实现强类型编程。

二、将XML表示的消息转换成VB.NET或者C#代码

可能有些人没有看过之前的文章,所以在这里我再次简单介绍一些我们需要通过代码生成机制实现的场景:无论对于怎么样的应用,我们都需要维护一系列的 消息。消息的类型很多,比如验证消息、确认消息、日志消息等。我们一般会将消息储存在一个文件或者数据库中进行维护,并提供一些API来获取相应的消息 项。这些API一般都是基于消息的ID来获取的,换句话说,消息获取的方式是以一种“弱类型”的编程方式实现的。如果我们能够根据消息存储的内容动态地生成相应的C#或者VB.NET代码,那么我们就能够以一种强类型的方式来获取相应的消息项了。

比如说,现在我们定义了如下一个MessageEntry类型来表示一个消息条目。为了简单,我们尽量简化MessageEntry的定义,仅仅保 留三个属性Id、Value和Category。Category表示该消息条目所属的类型,你可以根据具体的需要对其分类(比如根据模块名称或者 Severity等)。Value是一个消息真实的内容,可以包含一些占位符({0},{1},…{N})。通过指定占位符对用的值,最中格式化后的文本 通过Format返回。

   1: public class MessageEntry
   2: {
   3:     public string Id { get; private set; }
   4:     public string Value { get; private set; }
   5:     public string Category { get; private set; }
   6:     public MessageEntry(string id, string value, string category)
   7:     {
   8:         this.Id = id;
   9:         this.Value = value;
  10:         this.Category = category;
  11:     }
  12:     public string Format(params object[] args)
  13:     {
  14:         return string.Format(this.Value, args);
  15:     }
  16: }

现在我们所有的消息定义在如下一个XML文件中,<message>XML元素代码一个具体的MessageEntry,相应的属性(Attribute)和MessageEntry的属性(Property)相对应。

   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中,定义了两个类别(Validation和Confirmation)的三条MessageEntry。我们需要通过我们的代码生成工具生成一个包含如下C#代码的CS文件。

1: public class Messages {
2:     public class Validation {
3:         public static Artech.MessageCodeGenerator.MessageEntry MandatoryField = new Artech.MessageCodeGenerator.MessageEntry("MandatoryField", "The {0} is mandatory.", "Validation");
4:         public static Artech.MessageCodeGenerator.MessageEntry GreaterThan = new Artech.MessageCodeGenerator.MessageEntry("GreaterThan", "The {0} must be greater than {1}.", "Validation");
5:     }
6:     public class Confirmation {
7:         public static Artech.MessageCodeGenerator.MessageEntry ReallyDelete = new Artech.MessageCodeGenerator.MessageEntry("ReallyDelete", "Do you really want to delete the {0}.", "Confirmation");
8:     }
9: }

三、将XML转换成CodeDOM

实际BuildProvider也是采用CodeDOM来定义代码的结构,在这之前我已经创建了一个CodeGenerator类实现了如何加载具有上述结构的XML,并生成一个体现最终代码结构的CodeCompileUnit对象。该CodeGenerator的所有代码的定义如下。

   1: public class CodeGenerator
   2: {
   3:     public CodeCompileUnit BuildCodeObject(XmlDocument messages)
   4:     {
   5:         var codeObject = new CodeCompileUnit();
   6:         var codeNamespace = new CodeNamespace("Artech.CodeDomGenerator");
   7:         codeObject.Namespaces.Add(codeNamespace);
   8:         var codeType = new CodeTypeDeclaration("Messages");
   9:         codeNamespace.Types.Add(codeType);
  10:         GenerateCatetoryClasses(codeType, messages);
  11:         return codeObject;
  12:     }
  13:
  14:     private void GenerateCatetoryClasses(CodeTypeDeclaration root, XmlDocument messageDoc)
  15:     {
  16:         var messageEntries = messageDoc.GetElementsByTagName("message").Cast<XmlElement>();
  17:         var categories = (from element in messageEntries
  18:                           select element.Attributes["category"].Value).Distinct();
  19:
  20:         foreach (var category in categories)
  21:         {
  22:             var categoryType = new CodeTypeDeclaration(category);
  23:             root.Members.Add(categoryType);
  24:
  25:             foreach (var element in messageDoc.GetElementsByTagName("message").Cast<XmlElement>().
  26:               Where(element => element.Attributes["category"].Value == category))
  27:             {
  28:                 GenerateMessageProperty(element, categoryType);
  29:             }
  30:         }
  31:     }
  32:
  33:     private void GenerateMessageProperty(XmlElement messageEntry, CodeTypeDeclaration type)
  34:     {
  35:         string id = messageEntry.Attributes["id"].Value;
  36:         string value = messageEntry.Attributes["value"].Value;
  37:         string categotry = messageEntry.Attributes["category"].Value;
  38:
  39:         var field = new CodeMemberField(typeof(MessageEntry), id);
  40:         type.Members.Add(field);
  41:         field.Attributes = MemberAttributes.Public | MemberAttributes.Static;
  42:         field.InitExpression = new CodeObjectCreateExpression(
  43:              typeof(MessageEntry),
  44:              new CodePrimitiveExpression(id),
  45:              new CodePrimitiveExpression(value),
  46:              new CodePrimitiveExpression(categotry));
  47:     }
  48: }

四、自定义BuildProvider

现在我们才进行我们的重点,如何通过一个自定义的BuildProvider将以XML形式存储的消息列表转换成相应的C#或者VB.NET代码。 为此我们创建一个名为MessageBuildProvider的类,MessageBuildProvider继承自抽象类 BuildProvider。因为从XML到CodeDOM的转换已经实现在了上面的CodeGenerator类 中,MessageBuildProvider的定义很简单。

   1: public class MessageBuildProvider : BuildProvider
   2: {
   3:     public override void GenerateCode(AssemblyBuilder assemblyBuilder)
   4:     {
   5:         var messageDoc = new XmlDocument();
   6:         using (var stream = this.OpenStream())
   7:         {
   8:             messageDoc.Load(stream);
   9:         }
  10:         var codeObj = new CodeGenerator().BuildCodeObject(messageDoc);
  11:         assemblyBuilder.AddCodeCompileUnit(this, codeObj);
  12:     }
  13: }

五、BuildProvider的应用

自定义的BuildProvider以配置的方式和源文件的类型(扩展名),在这里我们通过一个扩展名为.msg(不代表OutLook的消息文 件)来表示上述的存储消息列表的XML。那么,你可以创建一个WebSite,并添加对定义了MessageBuildProvider的Dll引用或者 项目引用。然后添加一个XML文件,并将扩展名改成.msg,然后定义如下一段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>

然后在Web.config中添加如下一段配置以建立MessageBuildProvider和源文件扩展名(.msg)之间的匹配关系。

   1: <?xml version="1.0"?>
   2: <configuration>
   3:   <system.web>
   4:     <compilation debug="false" targetFramework="4.0" >
   5:       <buildProviders>
   6:         <add extension=".msg" type="Artech.MessageCodeGenerator.MessageBuildProvider, Artech.MessageCodeGenerator.Lib"/>
   7:       </buildProviders>
   8:     </compilation>
   9:   </system.web>
  10: </configuration>

然后,你在任何该WebSite范围类进行编程的时候,就可以利用VS的职能感知感受到相应的代码已经生成。

为什么说“感受”得到代码已经被成功生成呢?这是因为不象之前介绍的两种代码生成方式,会显式地创建一个.cs或者.vb物理文件,并自动添加到项 目文件。BuildProvider采用的是一种隐式代码生成机制。不过你通过Go to definition菜单可以得到整个生成代码的内容。如果你采用基于C#的WebSite,生成的代码时如下所示。由于CodeDOM的语言无关性,你 也可以将MessageBuildProvider用于基于VB.NET的ASP.NET应用。

转载于:https://my.oschina.net/lichaoqiang/blog/397176

提供第三种代码生成方式——通过自定义BuildProvider为ASP.NET提供代码生成相关推荐

  1. 三种代码版本控制系统的简介 以及 SVN的安装

    三种代码版本控制系统的简介 以及 SVN的安装 一.三种代码版本控制系统 1.1 CVS 什么是cvs??? 1.2.Git 什么是Git??? 1.3.SVN 什么是SVN??? SVN的作用??? ...

  2. 计算机内部组件三种电压,电源通常向计算机内部的各种组件提供哪三种电压

    此资源收集于兔客源码网www.tukebbs.com 电源通常向计算机内部的各种组件提供的三种电压:1.5V,主要供给主板:2.3.3V,主要为CPU供电:3.正负12V,主要为音频电路,硬盘供电.计 ...

  3. 网络上连接的计算机必须要安装,[单选] Windows XP中提供了三种组件,实现不同的网络功能。如果计算机需要连接到Internet,必须安装()。...

    [单选] Windows XP中提供了三种组件,实现不同的网络功能.如果计算机需要连接到Internet,必须安装(). 更多相关问题 [单选] 乙炔与空气或氧气混合达到自燃温度在()下也能爆炸 [单 ...

  4. 【 史上最坑爹的Java代码系列02 】Java中的三种代码块

    Java中有三种代码块,三种代码块中的代码执行的优先级是面试中会被提问到的问题. 三种代码块:静态代码块.方法代码块.普通代码块 其中,普通代码块是比较少见而且比较少使用的. package com. ...

  5. python提供的三种基本数据类型是()_python基础之基本数据类型

    1.概念 1.1 表达式 表达式,是由数字.算符.数字分组符号(括号).自由变量和约束变量等以能求得数值的有意义排列方法所得的组合 表达式特点 表达式一般仅仅用于计算一些结果,不会对程序产生实质性的影 ...

  6. c语言提供了三种预处理命令,9、C语言之预处理命令

    预处理命令 基本概念:ANSI C标准规定可以在C源程序中加入一些"预处理命令",以改进程序设计环境,提高编程效率. 这些预处理命令是由ANSI C同一规定的,但是它们不是C语言本 ...

  7. c语言提供了三种预处理命令,C语言提供的三种预处理命令

    C语言的三种预处理包括:宏定义(#define).文件包含(#include).条件编译(#if.#else.#endif). 1,不带参数的宏定义: #define 标示符 字符串 在预编译时将宏名 ...

  8. java random产生随机数_java的三种随机数生成方式,必掌握

    随机数的产生在一些代码中很常用,也是我们必须要掌握的.而java中产生随机数的方法主要有三种: 第一种:new Random() 第二种:Math.random() 第三种:currentTimeMi ...

  9. 深信服何朝曦:托管云为用户上云提供第三种选择

    9月17日,以"万物皆可云"为主题的信服云创新峰会成功举办.会上,深信服科技股份有限公司CEO何朝曦分享了运用云计算技术帮助用户进行数字化转型的观点.他认为兼具公有云和私有云优势的 ...

最新文章

  1. 树莓派 —— USB 摄像头简单测试 (拍照 视频)
  2. Server.UrlEncode UrlDecode 动态绑定gridview列发送接收乱码的问题
  3. dns轮训python
  4. Laravel核心解读 -- 用户认证系统(基础介绍)
  5. 基于固定坐标与基于参考坐标系得到的机械手的微分运动不同
  6. 百度推广怎么样做才可以有效果呢?
  7. 静态编译和动态编译的区别【转】
  8. vscode自定义代码片段
  9. [Windows小工具]两款屏幕取色拾色器(免安装,1MB左右大小)
  10. 初探JavaScript PDF blob转换为Word docx方法
  11. 淘宝客淘点金代码自动生成跳转
  12. 安装U8后服务器开机加载信息慢,用友U8 安装ADSL后,连接服务器非常慢
  13. Elasticsearch:使用向量搜索来查询及比较文字 - NLP text embedding
  14. android 生成bks_生成android的bks证书
  15. 关于取地址运算符以及指针10要点
  16. 推荐一个朋友 - 学历不好,非科班,不负光阴终进大厂
  17. 什么是硬件集成开发?硬件集成开发的核心有哪些?
  18. javaweb学习记录-qqzone项目-结构分析
  19. C/C++、OS、网络面经
  20. Java中用for循环输出九九乘法表

热门文章

  1. DNS域名解析优化之tinydns/djbdns篇——测试篇
  2. PoPo数据可视化第9期
  3. Go Pro 半小时上手指南
  4. node 模块化 require expores,简易实现原理。
  5. 转载:APP的上线和推广——线上推广渠道
  6. OSChina 周二乱弹 ——同意,我的元首大人
  7. ELK(+Redis)+LogAnalyzer解决企业日志问题
  8. Java Reflection(十):数组
  9. 使用getopt处理shell脚本的参数
  10. C++基本要点复习--------coursera程序设计实习(PKU)的lecture notes