当前位置: 首页 > 编程日记 > 正文

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

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

相关文章:

Java拦截过滤器模式

拦截过滤器模式&#xff08;Intercepting Filter Pattern&#xff09;用于对应用程序的请求或响应做一些预处理/后处理。定义过滤器&#xff0c;并在把请求传给实际目标应用程序之前应用在请求上。过滤器可以做认证/授权/记录日志&#xff0c;或者跟踪请求&#xff0c;然后把请…

1200亿次日均位置服务响应、20亿公里日均轨迹里程,百度地图发布新一代人工智能地图生态全景

12月10日&#xff0c;百度地图首次公布了“新一代人工智能地图”生态全景。目前&#xff0c;百度地图日均位置服务请求次数突破1200亿次&#xff0c;日均轨迹里程20亿公里&#xff0c;注册开发者数量达180万&#xff0c;服务超过50万个移动应用。 百度地图事业部总经理李莹称&…

Python3中global/nonlocal用法

全局变量(global variable)是那些未在任何函数内部定义并且具有全局作用域的变量&#xff0c;而局部变量(local variable)是那些在函数内部定义并且其作用域仅限于该函数的变量。换句话说&#xff0c;我们可以说局部变量只能在初始化它的函数内部访问&#xff0c;而全局变量在整…

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

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

Python3中闭包介绍

Python3中的闭包(closure)是一个函数对象&#xff0c;它记住封闭作用域(enclosing function)中的值&#xff0c;即使它们不存在于内存中。它是一个将函数与环境一起存储的记录。由于闭包用作回调函数&#xff0c;因此它们提供了某种数据隐藏&#xff0c;这有助于我们减少使用全…

Java服务定位器模式

服务定位器模式&#xff08;Service Locator Pattern&#xff09;用在我们想使用 JNDI 查询定位各种服务的时候。考虑到为某个服务查找 JNDI 的代价很高&#xff0c;服务定位器模式充分利用了缓存技术。在首次请求某个服务时&#xff0c;服务定位器在 JNDI 中查找服务&#xff…

用AI加速物联网落地,安富利的客户洞察和解决之道

作为一家全球IT解决方案分销商&#xff0c;成立于1921年的安富利也紧随AI浪潮&#xff0c;为区域和全球的终端客户提供AI解决方案。据了解&#xff0c;安富利已为全球超过1400家企业提供技术支持&#xff0c;电子产品年度出货量达1220亿片。 近日在媒体沟通会上&#xff0c;安富…

Linux运维工程师发展前景

随着IT产业的不断发展&#xff0c;尤其是Linux行业的发展&#xff0c;现在互联网企业服务器数量越来越多&#xff0c;当到达几百台&#xff0c;上千台服务器之后&#xff0c;服务器日常管理也逐渐繁杂&#xff0c;每天如果通过人工去频繁的更新或者部署及管理这些服务器&#x…

Python3中__init__.py文件介绍

Python中的模块是包含Python定义和语句的文件(A module is a file containing Python definitions and statements)&#xff0c;其文件名是模块名加后缀名.py。在模块内部&#xff0c;通过全局变量__name__可以获取模块名。 模块包含可执行语句及函数定义。这些语句用于初始化模…

赠书 | 熵的实际应用,赌场和金融圈最著名的一个数学公式

本文选自湛庐文化策划出版图书《模型思维》。作者斯科特佩奇&#xff0c;超过100万用户的“模型思维课”主讲人。密歇根大学复杂性研究中心“掌门人”。圣塔菲研究所外聘研究员。曾出版《多样性红利》一书。斯科特佩奇以对社会科学多样性和复杂性的研究和建模面闻名。具体研究方…

Java传输对象模式

传输对象模式&#xff08;Transfer Object Pattern&#xff09;用于从客户端向服务器一次性传递带有多个属性的数据。传输对象也被称为数值对象。传输对象是一个具有 getter/setter 方法的简单的 POJO 类&#xff0c;它是可序列化的&#xff0c;所以它可以通过网络传输。它没有…

图片下方出现几像素的空白间隙

1、如何定义高度很小的容器&#xff1f; 在IE6下无法定义小高度的容器&#xff0c;是因为有一个默认的行高。 列举2种解决方案&#xff1a;overflow:hidden | line-height:0 2、图片下方出现几像素的空白间隙&#xff1f; 这个也有多种解决方案&#xff0c;如将img定义为displa…

Python3中Pillow(PIL)介绍

PIL全称为Python Imaging Library&#xff0c;是Python中的免费开源图像处理库。PIL的最新版本为1.1.7&#xff0c;于2009年9月发布&#xff0c;支持Python的最高版本到2.7。原始的PIL开发于2011年停止。随后&#xff0c;一个名为Pillow的后续项目fork了PIL的repository并增加了…

GitHub有望在中国开设子公司?

作者 | Financial Times译者 | 弯月&#xff0c;编辑 | 郭芮出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;作为世界上最大的软件开发平台&#xff0c;GitHub 自去年被微软以 75 亿美元收购后&#xff0c;一直颇受外界的争议。虽然在交易完成后&#xff0c;GitHub…

OC指示符assign、atomic、nonatomic、copy、retain、strong、week的解释

在使用property定义property时可以在property与类型之间用括号添加一些额外的指示符&#xff0c;常用的指示符有assign、atomic、nonatomic、copy、retain、strong、week、等。详情如下&#xff1a; assign&#xff1a; 简单赋值&#xff0c;不更改索引计数&#xff08;Referen…

项目沟通管理计划

沟通计划包括决定项目涉及人的信息和沟通需求&#xff1a;谁需要什么信息&#xff1b;什么时候需要&#xff1b;怎么获得。虽然所有的项目都需要沟通项目信息&#xff0c;但信息需求和传播方式差别很大。确认涉及人的信息需求和决定满足需求的适当方式是项目获得成功的重要因素…

PyTorch中torchvision介绍

TorchVision包包含流行的数据集、模型架构和用于计算机视觉的图像转换&#xff0c;它是PyTorch项目的一部分。TorchVison最新发布版本为v0.11.1&#xff0c;发布较频繁&#xff0c;它的license为BSD-3-Clause。它的源码位于&#xff1a; https://github.com/pytorch/vision T…

百度ERNIE登顶GLUE榜单,得分首破90大关

出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;12月10日&#xff0c;百度ERNIE在自然语言处理领域权威数据集GLUE中登顶榜首&#xff0c;以9个任务平均得分首次突破90大关刷新该榜单历史&#xff0c;其表现超越微软MT-DNN-SMART, 谷歌T5、ALBERT等一众顶级预训练模…

Java 重写(Override)与重载(Overload)

TestDog.java /* * 重写(Override) * 重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变&#xff0c;核心重写&#xff01; * 重写的好处在于子类可以根据需要&#xff0c;定义特定于自己的行为。 也就是说子类能够根据需要实现…

Oracle常用查看表结构命令

2019独角兽企业重金招聘Python工程师标准>>> select user from dual; //查看当前的用户名 select table_name from all_tables; //所有用户的表 select table_name from dba_tables; //包括系统表 select table_name from dba_tables where owner用户名 user_tabl…

TorchVision中使用FasterRCNN+ResNet50+FPN进行目标检测

TorchVision中给出了使用ResNet-50-FPN主干(backbone)构建Faster R-CNN的pretrained模型&#xff0c;模型存放位置为https://download.pytorch.org/models/fasterrcnn_resnet50_fpn_coco-258fb6c6.pth&#xff0c;可通过fasterrcnn_resnet50_fpn函数下载&#xff0c;此函数实现…

iOS-UIButton防止重复点击(三种办法)

目录 使用场景方法一 设置enabled或userInteractionEnabled属性方法二 借助cancelPreviousPerformRequestsWithTarget:selector:object实现方法三 通过runtime交换方法实现注意事项一 使用场景 在实际应用场景中&#xff0c;有几个业务场景需要控制UIButton响应事件的时间间隔。…

华为诺亚方舟开源预训练模型“哪吒”,4项任务均达到SOTA

出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;BERT之后&#xff0c;新的预训练语言模型XLnet、RoBERTa、ERNIE不断推出&#xff0c;这次&#xff0c;华为诺亚方舟实验室开源了基于BERT的中文预训练语言模型NEZHA&#xff08;哪吒&#xff09;&#xff0c;寓意模型能…

音量调节助手(转)

源&#xff1a;音量调节助手 软件名称&#xff1a;音量调节助手 软件功能&#xff1a;通过键盘快捷键快速调节系统主音量 软件版本&#xff1a;V2014 软件作者&#xff1a;易几网络 操作系统&#xff1a;所有WINDOWS版本 开发工具&#xff1a;DELPHI XE …

TorchVision中通过AlexNet网络进行图像分类

TorchVision中给出了AlexNet的pretrained模型&#xff0c;模型存放位置为https://download.pytorch.org/models/alexnet-owt-4df8aa71.pth &#xff0c;可通过models.alexnet函数下载&#xff0c;此函数实现在torchvision/models/alexnet.py中&#xff0c;下载后在Ubuntu上存放…

西湖龙井也上链?是的,以后你喝什么茶我都知道!

什么&#xff1f;区块链还可以帮忙法律取证&#xff1f;是的&#xff01;就是这么牛13&#xff01;区块链存证第一案12月9日&#xff0c;据《新华每日电讯》报道&#xff0c;杭州互联网法院用区块链提升审判效率。报道提到一个案例。2018年4月&#xff0c;杭州一家公司&#xf…

Java Enumeration接口

import java.util.Vector; import java.util.Enumeration; /* * Enumeration接口中定义了一些方法&#xff0c;通过这些方法可以枚举&#xff08;一次获得一个&#xff09;对象集合中的元素。 * 这种传统接口已被迭代器取代&#xff0c;虽然Enumeration 还未被遗弃&#xff0…

Windows Azure Pack与SCVMM标签解析分享

我在SCVMM上做了好CentOS6.5的VM模板镜像&#xff0c;自己部署也是成功的&#xff0c;现在配置WAP的VM云虚拟机角色配置&#xff0c;在SCVMM上我打好了CentOS6.5的标签&#xff0c;可是在创建虚拟机角色配置中&#xff0c;选择的CentOS却无法找到硬盘&#xff0c;这是怎么回事呢…

Linux下C++中可使用的3种Hook方法

Hook即钩子&#xff0c;截获API调用的技术&#xff0c;是将执行流程重定向到你自己的代码&#xff0c;类似于hack。如使程序运行时调用你自己实现的malloc函数代替调用系统库中的malloc函数。这里介绍下Linux下C中可使用的3中Hook方法&#xff1a; 1. GNU C库允许你通过指定适当…

Java Properties 类

Properties 继承于 Hashtable.表示一个持久的属性集.属性列表中每个键及其对应值都是一个字符串。 Properties 类被许多Java类使用。例如&#xff0c;在获取环境变量时它就作为System.getProperties()方法的返回值。 Properties 定义如下实例变量.这个变量持有一个Properties对…