从数据到代码——通过代码生成机制实现强类型编程[上篇]
我不知道大家对CodeDOM的代码生成机制是否熟悉,但是有一点可以确定:如果你使用过Visual Studio,你就应该体验过它带给我们在编程上的便利。随便列举三种典型的代码生成的场景:在创建强类型DataSet的时候,VS会自动根据Schema生成相应的C#或者VB.NET代码;当我们编辑Resource文件的时候,相应的的后台代码也会自动生成;当我们通过添加Web Reference调用Web Service或者WCF Service的时候,VS会自动生成服务代理的代码和相应的配置。总的来说,通过和VS集成的动态代码生成工具使我们可以“强类型”的方式进行编程,进而提供我们的效率并减低错误的几率。 实际上,除了VS提供的这些典型的代码生成场景中,我们可以根据需要开发一些自定义代码生成器,并且通过VS的扩展实现后台代码的实时生成,从而实现强类型编程的目的,现在我们举一个典型的应用场景——消息管理。 一、一个典型的自定义代码生成器应用场景——消息管理无论对于怎么样的应用,我们都需要维护一系列的消息。消息的类型很多,比如验证消息、确认消息、日志消息等。我们一般会将消息储存在一个文件或者数据库中进行维护,并提供一些API来获取相应的消息项。这些API一般都是基于消息的ID来获取的,换句话说,消息获取的方式是以一种“弱类型”的编程方式实现的。如果我们能够根据消息存储的内容动态地生成相应的C#或者VB.NET代码,那么我们就能够以一种强类型的方式来获取相应的消息项了。 比如说,现在我们定义了如下一个MessageEntry类型来表示一个消息条目。为了简单,我们尽量简化MessageEntry的定义,仅仅保留三个属性Id、Value和Category。Category表示该消息条目所属的类型,你可以根据具体的需要对其分类(比如根据模块名称或者Severity等)。Value是一个消息真实的内容,可以包含一些占位符({0},{1},…{N})。通过指定占位符对用的值,最中格式化后的文本通过Format返回。 1: public class MessageEntry 3: string Id { get; private set; } 5: string Category { get; 6: public MessageEntry(string id,string value,1)">string category) 8: this.Id = id;
10: this.Category = category;
12: string Format(params object[] args) 14: return string.Format(this.Value,args); 16: } 现在我们所有的消息定义在如下一个XML文件中,<message>XML元素代码一个具体的MessageEntry,相应的属性(Attribute)和MessageEntry的属性(Property)相对应。 2: <messages> 4: ="GreaterThan" ="The {0} must be greater than {1}." /> namespace Artech.CodeDomGenerator class Messages 5: class Validation
7: static Artech.CodeDomGenerator.MessageEntry MandatoryField = new Artech.CodeDomGenerator.MessageEntry("MandatoryField",1)">"The {0} is mandatory.",1)">"Validation"); 9: } 11: { 13: } 15: } 那么我们就能够直接通过生成出来的Messages类,以强类型的方式获取并格式化每一条MessageEntry的内容了。 2: Console.WriteLine(Messages.Validation.GreaterThan.Format("Age",18));
1: The User Name is mandatory.
3: Do you really want to delete the Order record. 要实现上面的功能实际上包含两个步骤:一是动态解析包含消息定义的XML文件,并生成我们希望结构的一个代码定义,而是通过和VS进行集成,借助VS自定义工具将前面生成的内容真正写入到一个具体的.cs文件中。第一个步骤可以通过CodeDOM轻松实现,而第二个步骤借助于VS的扩展也会很简单。本篇文章我们只关注第一个方面,下面我们在对第二个方面进行介绍。 二、通过CodeDom实现动态代码生成CodeDOM 提供了表示许多常见的源代码元素类型的类型。您可以设计一个生成源代码模型的程序,使用CodeDOM 元素构成一个对象图。而这个对象图包含C#或者VB.NET代码包含的基本元素:命名空间、类型、类型成员(方法、属性、构造函数、事件等),并且包括方法实现的具体语句(Statement)。也就是说它的结构就是对一个具体.vb或者.cs文件代码的反映。在这里我不会具体介绍CodeDOM体系结构,有兴趣的读者可以参与MSDN官方文档。 CodeDOM最终体现出来的是一个叫做CodeCompileUnit对象,这个对象通过如下定义的MessageCodeGnerator的BuildCodeObject方法返回。下面给出了生成CodeCompileUnit的全部实现,即使你对CodeDOM完全不了解,结合上面给出的保存消息的XML和我们最终期望的C#代码的结构,相信也能够看懂整个实现逻辑。 总的来说,BuildCodeObject方法的目的就是一个将XML转换成CodeCompileUnit对象。首先在BuildCodeObject方法中,添加了一个命名空间(Artech.CodeDomGenerator),并在该命名空间中定义了一个Messages的类。在Messages类会为每一个消息类别定义一个嵌套类,类型的名称就是消息类别的名称(比如Validation、Confirmation等)。我们具体的MessageEntry通过公共静态属性的形式进行定义,并且采用Inline的方式进行初始化。 public CodeCompileUnit BuildCodeObject(XmlDocument messages)
5: var codeObject = new CodeCompileUnit();
7: codeObject.Namespaces.Add(codeNamespace); 9: codeNamespace.Types.Add(codeType); 11: 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: } 1: var generator = new MessageCodeGenerator();
3: messageDoc.Load("Messages.xml");
5: CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
7: using (StreamWriter writer = new StreamWriter("messages.cs")) 9: provider.GenerateCodeFromCompileUnit(codeObject,writer,options); 11:? 13: "messages.vb"))
15: provider.GenerateCodeFromCompileUnit(codeObject,options); 17:? 19: Process.Start("messages.vb");
这是C#代码(和我们开始提到过的完全一致): // <auto-generated>
4: // Runtime Version:4.0.30319.1
// Changes to this file may cause incorrect behavior and will be lost if 8: // </auto-generated>
11: namespace Artech.CodeDomGenerator {
13: 15: 17: 20: 21: }
23: class Confirmation {
"Confirmation"); 27: } '------------------------------------------------------------------------------ 12: Option Explicit On 15: Namespace Artech.CodeDomGenerator
17: Public Class Messages 19: Class Validation
21: Shared MandatoryField As Artech.CodeDomGenerator.MessageEntry = New Artech.CodeDomGenerator.MessageEntry("Validation") 23: Shared GreaterThan 24: End Class 26: Class Confirmation
28: Shared ReallyDelete "Confirmation") 30: 31: Namespace ?在《下篇》中,我们将着重介绍如果通过VS的扩展实现如何将我们的MessageCodeGenerator和XML进行绑定,使XML内容改变的时候,相应的代码能够动态的生成。? 从数据到代码——通过代码生成机制实现强类型编程[上篇] ?
作者:Artech
出处:http://artech.cnblogs.com 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- asp.net-mvc – 如何将我的对象保存回LINQ to SQL中的数据库
- asp.net – X509Certificate2 – 系统找不到指定的路径
- asp.net – 如何扩展aspnet成员身份验证表?
- vbscript – 服务器端注释:ASP Classic中相当于<% - - %
- asp.net – 将图像转换为流
- asp.net – AJAX和FormsAuthentication,如何防止FormsAuthe
- asp.net-core – ASP.NET核心添加视图参数名称路径不能为空
- 如何使用asp.net在服务器上生成新的html页面?
- asp.net-mvc – 为什么ASP.NET MVC Html.CheckBox输出两个I
- asp.net – 通过VB.NET将click事件添加到按钮以激活javascr
- [ASP.NET MVC] 产生一维条码Barcode(Code 39、Co
- asp.net-mvc-3 – 将数组传递给RouteValues,并将
- Azure上的Asp.Net Core 2.0产生了502.5
- 如何在ASP.NET应用程序中有效地缩放和裁剪图像?
- asp.net-mvc – 理解和使用“服务层” – .NET M
- 为什么我的客户端去服务器来检查在使用ASP.NET M
- asp.net-mvc-5 – 什么是XsrfKey用于,我应该将Xs
- ASP.NET Core和Angular的RDLC本地报表查看器(> 2
- asp.net – 生成PDF,IE和HTTPS错误
- 如何打印与phpinfo()但ASP.NET类似的信息?