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

news/2024/7/5 1:37:46

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

之前写了一些关于代码生成的文章,提供了两种不同方式的代码生成解决方案,即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进行源代码的生成。比如UserControlBuildProviderMasterPageBuildProvider分 别实现了基于用户控件文件(.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的职能感知感受到相应的代码已经生成。

image

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

image


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


http://lihuaxi.xjx100.cn/news/260534.html

相关文章

汇编语言(王爽 第三版) ret retf总结 以及检测点10.1

关键导读&#xff1a; ret对应的代码以及结果: assume cs:codesgstack segmentdb 16 dup(0) stack endscodesg segmentmov ax,4c00hint 21h start:mov ax,stackmov ss,axmov sp,16mov ax,0push axmov bx,0ret;执行之后就是将IP修改成为栈顶所对应的数据,再执行出栈(SPSP2) cod…

赠书 | 图解机器学习算法,看这文就够了!

机器学习指的是计算机根据给定的问题、课题或环境进行学习&#xff0c;并利用学习结果解决问题或课题等的一整套机制&#xff08;图 1-1&#xff09;。 图 1-1 分类的示意图与机器学习共同成为热门话题的还有人工智能和深度学习。这里梳理一下它们之间的关系&#xff08;图 1-…

ACL 2022丨香港大学华为诺亚方舟新工作:生成式预训练语言模型的量化压缩

点击上方“视学算法”&#xff0c;选择加"星标"或“置顶”重磅干货&#xff0c;第一时间送达近日&#xff0c;香港大学与华为诺亚方舟实验室在 ACL 2022 上联合发表了一篇工作&#xff0c;针对现有大规模生成式预训练语言模型的压缩需求&#xff0c;提出了新的量化压…

GitHub 的微服务架构设计与实践

以下文章来源方志朋的博客&#xff0c;回复”666“获面试宝典本文介绍 GtHub 如何从单体架构迁移到微服务架构&#xff0c;并对其中一些最佳实践做了详细说明。1 旅程开启GitHub 创建于 2008 年&#xff0c;其宗旨是为开发人员托管和分享代码提供便利。GitHub 的创建者也是开源…

call ret指令的总结

ret:pop IP retf:pop ip&#xff1b;pop cs call 标号&#xff1a;push ip &#xff1b;jmp near ptr 标号 call far ptr 标号&#xff1a;push cs&#xff1b;push ip&#xff1b;jmp far ptr 标号 call 16位 reg&#xff1a;push ip&#xff1b;jmp 16位 reg call word…

客户端动态调用WCF服务中的方法

首先要写一个执行动态调用的方法&#xff1a;在里面实现反射调用。 public static object ExecuteMethod<T>(string pUrl,string pMethodName, params object[] pParams) { EndpointAddress address new EndpointAddress(pUrl); Binding bindinginstance null; NetTcpB…

深度学习之Pytorch基础教程!

↑↑↑关注后"星标"Datawhale每日干货 & 每月组队学习&#xff0c;不错过Datawhale干货 作者&#xff1a;李祖贤&#xff0c;Datawhale高校群成员&#xff0c;深圳大学随着深度学习的发展&#xff0c;深度学习框架开始大量的出现。尤其是近两年&#xff0c;Googl…

springMVC入门截图

mvc在bs系统下的应用 ---------------------------------------------------- 在web.xml中配置前端控制器&#xff08;系统提供的一个servlet类 只需配置即可 无需程序员开发 &#xff09; -------------------------------------------------------------- ----------------…