zl程序教程

您现在的位置是:首页 >  后端

当前栏目

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

NetASP 方式 通过 自定义 提供 代码生成
2023-09-27 14:27:57 时间

对于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"? 

 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"/ 

 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"/ 

既然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返回。


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


 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"/ 

在上面的XML中,定义了两个类别(Validation和Confirmation)的三条MessageEntry。我们需要通过我们的代码生成工具生成一个包含如下C#代码的CS文件。


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"); 

7: public static Artech.MessageCodeGenerator.MessageEntry ReallyDelete = new Artech.MessageCodeGenerator.MessageEntry("ReallyDelete", "Do you really want to delete the {0}.", "Confirmation"); 


三、将XML转换成CodeDOM

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


 14: private void GenerateCatetoryClasses(CodeTypeDeclaration root, XmlDocument messageDoc)

 16: var messageEntries = messageDoc.GetElementsByTagName("message").Cast XmlElement 

 25: foreach (var element in messageDoc.GetElementsByTagName("message").Cast XmlElement ().

 33: private void GenerateMessageProperty(XmlElement messageEntry, CodeTypeDeclaration type)


四、自定义BuildProvider

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



五、BuildProvider的应用

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


 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"/ 

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


 6: add extension=".msg" type="Artech.MessageCodeGenerator.MessageBuildProvider, Artech.MessageCodeGenerator.Lib"/ 

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

image

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

image

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

ASP.NET Core: 二十一. 内容协商与自定义IActionResult和格式化类(五) 上一章的结尾留下了一个问题:同样是ObjectResult,在执行的时候又是如何被转换成string和JSON两种格式的呢? 本章来解答这个问题,这里涉及到一个名词:“内容协商”。除了这个,本章将通过两个例子来介绍如何自定义IActionResult和格式化类。
ASP.NET Core: 二十一. 内容协商与自定义IActionResult和格式化类(四) 上一章的结尾留下了一个问题:同样是ObjectResult,在执行的时候又是如何被转换成string和JSON两种格式的呢? 本章来解答这个问题,这里涉及到一个名词:“内容协商”。除了这个,本章将通过两个例子来介绍如何自定义IActionResult和格式化类。
ASP.NET Core: 二十一. 内容协商与自定义IActionResult和格式化类(三) 上一章的结尾留下了一个问题:同样是ObjectResult,在执行的时候又是如何被转换成string和JSON两种格式的呢? 本章来解答这个问题,这里涉及到一个名词:“内容协商”。除了这个,本章将通过两个例子来介绍如何自定义IActionResult和格式化类。
ASP.NET Core: 二十一. 内容协商与自定义IActionResult和格式化类(二) 上一章的结尾留下了一个问题:同样是ObjectResult,在执行的时候又是如何被转换成string和JSON两种格式的呢? 本章来解答这个问题,这里涉及到一个名词:“内容协商”。除了这个,本章将通过两个例子来介绍如何自定义IActionResult和格式化类。
ASP.NET Core: 二十一. 内容协商与自定义IActionResult和格式化类(一) 上一章的结尾留下了一个问题:同样是ObjectResult,在执行的时候又是如何被转换成string和JSON两种格式的呢? 本章来解答这个问题,这里涉及到一个名词:“内容协商”。除了这个,本章将通过两个例子来介绍如何自定义IActionResult和格式化类。
ASP.NET Core Identity自定义数据库结构和完全使用Dapper而非EntityFramework Core ASP.NET Core Identity自定义数据库结构和完全使用Dapper而非EntityFramework Core前言原本本节内容是不存在的,出于有几个人问到了我:我想使用ASP.NET Core Identity,但是我又不想使用默认生成的数据库表,想自定义一套,我想要使用ASP.NE Core Identity又不想使用EntityFramework Core。
ASP.NET Core - 实现自定义WebApi模型验证 ASP.NET Core - 实现自定义WebApi模型验证 Framework时代 在Framework时代,我们一般进行参数验证的时候,以下代码是非常常见的 [HttpPost] public async Task JsonResult SaveNewCus...