一起谈.NET技术,提供第三种代码生成方式——通过自定义BuildProvider为ASP.NET提供代码生成

简介:   之前写了一些关于代码生成的文章,提供了两种不同方式的代码生成解决方案,即CodeDOM+Custom Tool和T4。对于ASP.NET应用,你还有第三种选择——自定义BuildProvider。[文中涉及的源代码从这里下载]目录 一、BuildProvider是什么? 二、将XML表示的消息转换成VB.

  之前写了一些关于代码生成的文章,提供了两种不同方式的代码生成解决方案,即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的职能感知感受到相应的代码已经生成。

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

image

目录
相关文章
|
监控 Cloud Native 测试技术
.NET技术深度解析:现代企业级开发指南
每日激励:“不要一直责怪过去的自己,他曾经站在雾里也很迷茫”。我是蒋星熠Jaxonic,一名在代码宇宙中探索的极客旅人。从.NET Framework到.NET 8,我深耕跨平台、高性能、云原生开发,践行领域驱动设计与微服务架构,用代码书写技术诗篇。分享架构演进、性能优化与AI融合前沿,助力开发者在二进制星河中逐光前行。关注我,共探技术无限可能!
.NET技术深度解析:现代企业级开发指南
|
JSON 安全 API
.net 自定义日志类
在.NET中,创建自定义日志类有助于更好地管理日志信息。示例展示了如何创建、配置和使用日志记录功能,包括写入日志文件、设置日志级别、格式化消息等。注意事项涵盖时间戳、日志级别、JSON序列化、线程安全、日志格式、文件处理及示例使用。请根据需求调整代码。
404 13
|
SQL 小程序 API
如何运用C#.NET技术快速开发一套掌上医院系统?
本方案基于C#.NET技术快速构建掌上医院系统,结合模块化开发理念与医院信息化需求。核心功能涵盖用户端的预约挂号、在线问诊、报告查询等,以及管理端的排班管理和数据统计。采用.NET Core Web API与uni-app实现前后端分离,支持跨平台小程序开发。数据库选用SQL Server 2012,并通过读写分离与索引优化提升性能。部署方案包括Windows Server与负载均衡设计,确保高可用性。同时针对API差异、数据库老化及高并发等问题制定应对措施,保障系统稳定运行。推荐使用Postman、Redgate等工具辅助开发,提升效率与质量。
737 0
|
开发框架 算法 .NET
C#/.NET/.NET Core技术前沿周刊 | 第 15 期(2024年11.25-11.30)
C#/.NET/.NET Core技术前沿周刊 | 第 15 期(2024年11.25-11.30)
382 6
|
开发框架 Cloud Native .NET
C#/.NET/.NET Core技术前沿周刊 | 第 16 期(2024年12.01-12.08)
C#/.NET/.NET Core技术前沿周刊 | 第 16 期(2024年12.01-12.08)
432 6
|
自然语言处理 物联网 图形学
.NET 技术凭借其独特的优势和特性,为开发者们提供了一种高效、可靠且富有创造力的开发体验
本文深入探讨了.NET技术的独特优势及其在多个领域的应用,包括企业级应用、Web应用、桌面应用、移动应用和游戏开发。通过强大的工具集、高效的代码管理、跨平台支持及稳定的性能,.NET为开发者提供了高效、可靠的开发体验,并面对技术更新和竞争压力,不断创新发展。
836 7
|
开发框架 安全 .NET
在数字化时代,.NET 技术凭借跨平台兼容性、丰富的开发工具和框架、高效的性能及强大的安全稳定性,成为软件开发的重要支柱
在数字化时代,.NET 技术凭借跨平台兼容性、丰富的开发工具和框架、高效的性能及强大的安全稳定性,成为软件开发的重要支柱。它不仅加速了应用开发进程,提升了开发质量和可靠性,还促进了创新和业务发展,培养了专业人才和技术社区,为软件开发和数字化转型做出了重要贡献。
485 5
|
机器学习/深度学习 人工智能 物联网
.NET 技术:引领未来开发潮流
.NET 技术以其跨平台兼容性、高效的开发体验、强大的性能表现和安全可靠的架构,成为引领未来开发潮流的重要力量。本文深入探讨了 .NET 的核心优势与特点,及其在企业级应用、移动开发、云计算、人工智能等领域的广泛应用,展示了其卓越的应用价值和未来发展前景。
390 5
|
传感器 人工智能 供应链
.NET开发技术在数字化时代的创新作用,从高效的开发环境、强大的性能表现、丰富的库和框架资源等方面揭示了其关键优势。
本文深入探讨了.NET开发技术在数字化时代的创新作用,从高效的开发环境、强大的性能表现、丰富的库和框架资源等方面揭示了其关键优势。通过企业级应用、Web应用及移动应用的创新案例,展示了.NET在各领域的广泛应用和巨大潜力。展望未来,.NET将与新兴技术深度融合,拓展跨平台开发,推动云原生应用发展,持续创新。
297 4
|
开发框架 .NET C#
.NET 技术凭借高效开发环境、强大框架支持及跨平台特性,在软件开发中占据重要地位
.NET 技术凭借高效开发环境、强大框架支持及跨平台特性,在软件开发中占据重要地位。从企业应用到电子商务,再到移动开发,.NET 均展现出卓越性能,助力开发者提升效率与项目质量,推动行业持续发展。
544 4