提供第三种代码生成方式——通过自定义BuildProvider为ASP.NET提
之前写了一些关于代码生成的文章,提供了两种不同方式的代码生成解决方案,即CodeDOM+Custom Tool和T4。对于ASP.NET应用,你还有第三种选择——自定义BuildProvider。[文中涉及的源代码从这里下载]
一、BuildProvider是什么?对于ASP.NET应用的开发者来说,你可能不知道什么是BuildProvider,但是你几乎无时无刻不在使用它所带来的代码生成机制。当你创建一个.aspx文件的时候,为什么会自动创建对应源代码?当你在该.aspx页面中以XML的方式添加一个按钮,源代码中为什么会自动添加一个同名的属性。实际上,ASP.NET就是通过一个特殊的BuildProvider实现了将.aspx文件内容转换成相应的源代码,这个特殊的.aspx文件就是:PageBuildProvider。基于不同的文件类型,ASP.NET会采用不同的BuildProvider进行源代码的生成。比如UserControlBuildProvider和MasterPageBuildProvider分别实现了基于用户控件文件(.ascx)和母板页(.master)的源代码生成。你可以通过查看%Windows%Microsoft.NETFrameworkv2.0.50727CONFIGweb.config看看在默认情况下使用的BuildProvider以及它基于的源文件类型(扩展名)。 1: <?xml version="1.0" encoding="utf-8"?> 3: system.web> 5: compilation 6: buildProviders 7: add extension=".aspx" type="System.Web.Compilation.PageBuildProvider"/> 9: =".master" ="System.Web.Compilation.MasterPageBuildProvider" 10: =".asmx" ="System.Web.Compilation.WebServiceBuildProvider" 11: =".ashx" ="System.Web.Compilation.WebHandlerBuildProvider" 12: =".soap" 13: =".resx" ="System.Web.Compilation.ResXBuildProvider" 14: =".resources" ="System.Web.Compilation.ResourcesBuildProvider" 15: =".wsdl" ="System.Web.Compilation.WsdlBuildProvider" 16: =".xsd" ="System.Web.Compilation.XsdBuildProvider" 17: =".js" ="System.Web.Compilation.ForceCopyBuildProvider" 18: =".lic" ="System.Web.Compilation.IgnoreFileBuildProvider" 19: =".licx" 20: =".exclude" 21: =".refresh" 22: =".svc" ="System.ServiceModel.Activation.ServiceBuildProvider,System.ServiceModel,Version=3.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089" 23: =".xoml" ="System.ServiceModel.Activation.WorkflowServiceBuildProvider,System.WorkflowServices,Version=3.5.0.0,PublicKeyToken=31bf3856ad364e35" 24: </ 25: 26: 27: > 既然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返回。 2: { 4: string Value { get; private set; } 7: { this.Value = value;
11: } 13: { 15: } ="utf-8" ?messagesmessage id="MandatoryField" value="The {0} is mandatory." category="Validation" 4: ="GreaterThan" ="The {0} must be greater than {1}." 5: ="ReallyDelete" ="Do you really want to delete the {0}." ="Confirmation" 6: > 在上面的XML中,定义了两个类别(Validation和Confirmation)的三条MessageEntry。我们需要通过我们的代码生成工具生成一个包含如下C#代码的CS文件。 1: class Messages {
2: class Validation {
3: static Artech.MessageCodeGenerator.MessageEntry MandatoryField = new Artech.MessageCodeGenerator.MessageEntry("MandatoryField",1)">"The {0} is mandatory.",1)">"Validation"); 4: static Artech.MessageCodeGenerator.MessageEntry GreaterThan = "GreaterThan",1)">"The {0} must be greater than {1}.",1)">"Validation"); 5: } 6: class Confirmation {
7: static Artech.MessageCodeGenerator.MessageEntry ReallyDelete = "ReallyDelete",1)">"Do you really want to delete the {0}.",1)">"Confirmation"); 8: } 9: } 三、将XML转换成CodeDOM实际BuildProvider也是采用CodeDOM来定义代码的结构,在这之前我已经创建了一个CodeGenerator类实现了如何加载具有上述结构的XML,并生成一个体现最终代码结构的CodeCompileUnit对象。该CodeGenerator的所有代码的定义如下。 public CodeCompileUnit BuildCodeObject(XmlDocument messages)
5: var codeObject = new CodeCompileUnit();
7: codeObject.Namespaces.Add(codeNamespace); 9: codeNamespace.Types.Add(codeType); return codeObject; 13:? 15: { 17: var categories = (from element in messageEntries
19:? 21: { 23: root.Members.Add(categoryType); 25: foreach (var element in messageDoc.GetElementsByTagName("message").Cast<XmlElement>(). 27: { 29: } 31: } 33: void GenerateMessageProperty(XmlElement messageEntry,CodeTypeDeclaration type)
35: string id = messageEntry.Attributes["id"].Value; 37: string categotry = messageEntry.Attributes["category"].Value; 39: var field = new CodeMemberField(typeof(MessageEntry),id); 41: field.Attributes = MemberAttributes.Public | MemberAttributes.Static; 43: 44: new CodePrimitiveExpression(id), 47: } class MessageBuildProvider : BuildProvider 5: var messageDoc = new XmlDocument();
7: { 9: } 11: assemblyBuilder.AddCodeCompileUnit(this,codeObj);
> 然后在Web.config中添加如下一段配置以建立MessageBuildProvider和源文件扩展名(.msg)之间的匹配关系。 compilation debug="false" targetFramework="4.0" 5: =".msg" ="Artech.MessageCodeGenerator.MessageBuildProvider,Artech.MessageCodeGenerator.Lib" 7: 8: 9: 10: >
然后,你在任何该WebSite范围类进行编程的时候,就可以利用VS的职能感知感受到相应的代码已经生成。 为什么说“感受”得到代码已经被成功生成呢?这是因为不象之前介绍的两种代码生成方式,会显式地创建一个.cs或者.vb物理文件,并自动添加到项目文件。BuildProvider采用的是一种隐式代码生成机制。不过你通过Go to definition菜单可以得到整个生成代码的内容。如果你采用基于C#的WebSite,生成的代码时如下所示。由于CodeDOM的语言无关性,你也可以将MessageBuildProvider用于基于VB.NET的ASP.NET应用。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- .net – 如何从用户控件中引用母版页内容控件?
- asp.net – 在Application_BeginRequest中设置会话变量
- MVC/ASP.NET设计模板
- 在ASP.NET中检测来自移动浏览器的请求
- asp.net – 在资源文件中存储SQL查询是不好的做法吗?
- webmatrix – ASP.NET页面中的_PageStart.cshtml与_AppStar
- asp.net 禁用viewstate在web.config里
- asp.net-mvc-3 – 默认路由不起作用
- Razor 页面简化了 ASP.NET MVC 应用程序
- asp.net-mvc – asp.net mvc:TryUpdateModel返回值还是Mod
- 在WebApi Core ConfigureServices中访问services
- asp.net-mvc-2 – 从MCV2视图中的模型集合中读取
- asp.net-mvc – MVC脚手架 – 参考程序集中缺少类
- 我应该使用ASP.NET构建我的网站以利用我的C#知识
- ef-code-first – 如何使用LocalDB和EF,而不使用
- asp.net-mvc – 如何在asp.net mvc 3中使用@html
- asp.net-core – 如何在ASP.NET Core中启动Quart
- Asp.net C#具有特殊字符的电子邮件地址
- 一句代码实现批量数据绑定[下篇]
- asp.net – HttpContext.Current不解析在MVC 4项