当前位置:编程学习 > asp >>

NET应用框架架构设计实践 - 为应用程序框架提供多样化的配置方式

 

 

Microsoft .NET为应用程序开发人员提供了丰富的处理配置数据的编程模型与类型库。拥有这些组件,开发人员及用户可以方便地在无需重新编译应用程序的情形下,通过对配置数据的设置,对应用程序的执行行为与结果进行干预,从而使得相同的应用程序能够在不改变源程序的情况下,满足不同应用场景的特殊需求。就应用程序框架而言,在大多数情况下,开发人员也需要能够对其进行配置,以便获得不同的框架功能特性。比如,我们会在.NET应用程序的配置文件中加入对NHibernate框架的配置数据,如果应用程序还用到了Microsoft Patterns & Practices Enterprise Library(EntLib)的话,还需要加入对EntLib的配置数据,以使得各种不同的框架能够满足应用程序正常运行的需要。因此,对配置数据的读取、管理和使用,是每个应用程序框架所必备的功能。

在此我们不打算进一步细究基于Microsoft .NET的应用程序是如何读取和管理配置数据的。做过.NET应用程序开发的读者朋友都知道,配置数据都是写在应用程序的配置文件里的,比如app.config或者web.config,然后使用System.Configuration命名空间下提供的类来读取配置文件以获得配置数据;也可以使用ConfigurationSection、ConfigurationElement等类来定义应用程序自己的配置数据结构。有关这部分内容请读者朋友自己参阅相关文档或者网页资料。需要说明的是,app.config也好,web.config也好,都是基于AppDomain的,具体表现是:在可执行应用程序中,app.config以.exe.config或者.dll.config的扩展名形式,与主程序并存在同一个目录下;在Web应用程序中,web.config则在虚拟目录的根目录下。当然,这些都是一般情况,事实上.NET是允许开发人员改变app.config或者web.config的文件名的。

现在,让我们来简单地考虑一下单体测试工具的实现,这是件非常有趣的事情。最简单的方式就是,在这个单体测试工具中提供一些用于指定测试类型和测试方法的Attributes,比如类似Microsoft Visual Studio 2010单体测试框架中的TestClassAttribute、TestInitializeAttribute、TestMethodAttribute等,然后创建一个用于单体测试的Class Library,在这个Class Library中使用这些Attributes定义测试类型以及测试方法。接着,你会使用一个基于Windows Forms或者WPF的测试工具来加载这个Class Library,通过.NET反射技术读取Class Library中所有的测试类型与测试方法并执行这些方法。在执行的过程中,使用try…catch捕获来自Assert的异常以此判断单体测试是否执行通过。这个过程看似简单,但实际上还是有不少细节是需要仔细琢磨的,其中最重要的一个问题就是配置文件。如果你需要测试的仅仅是一些数值运算或者算法,那恭喜你,你无须为这个配置文件的问题而烦恼;但如果你需要测试的是一个应用程序框架,而这个框架的运行是需要依赖一些配置数据的,那你就会头疼了:我应该把这些配置数据写在哪里?是写在这个测试工具的配置文件里吗?肯定不合理,否则每次做测试前就需要对测试工具的配置文件进行修改;那我应该写在Class Library的配置文件里?对不起,这样做不奏效,因为测试工具会将Class Library加载到自己的AppDomain中,于是Class Library就无法读取它自己的配置文件了。

要解决这个问题,就需要变更测试工具对Class Library的加载方式,由简单的Assembly.Load方式转换成将其加载到另一个AppDomain中。在创建这个新的AppDomain时,使用AppDomainSetup.ConfigurationFile来设置配置文件,类似代码如下:

 

01    private void LoadRemoteProxy()

02    {

03        AppDomainSetup ads = new AppDomainSetup();

04        ads.ApplicationBase = Path.GetDirectoryName(this.AssemblyFile);

05        ads.ApplicationName = Path.GetFileName(this.AssemblyFile);

06        string configFileName = this.AssemblyFile + ".config";

07        if (File.Exists(configFileName))

08        {

09            ads.ConfigurationFile = configFileName;

10        }

11        ads.DisallowBindingRedirects = false;

12        ads.DisallowCodeDownload = true;

13        ads.ShadowCopyFiles = "true";

14        ads.ShadowCopyDirectories = Environment.GetFolderPath(Environment.SpecialFolder.InternetCache);

15        clientDomain = AppDomain.CreateDomain(Guid.NewGuid().ToString(), null, ads);

16        object[] args = new object[]{

17            this.AssemblyFile,

18            typeof(TestClassAttribute), typeof(ClassInitializeAttribute),

19            typeof(ClassCleanupAttribute), typeof(TestInitializeAttribute),

20            typeof(TestCleanupAttribute), typeof(TestMethodAttribute)

21        };

22        IRemoteProxy p = (IRemoteProxy)clientDomain

23        .CreateInstanceAndUnwrap(

24            typeof(IRemoteProxy).Assembly.FullName,

25            "VisualBenchmark.Proxies.RemoteProxy", false, 0, null, args, null, null);

26        if (p != null)

27        {

28            this.AssemblyName = p.AssemblyName;

29            this.Proxy = p;

30        }

31    }

 

 

这段代码来自之前我所写的一个基于单体测试的性能比较工具Visual Benchmark。该工具的主页地址是:http://visualbenchmark.codeplex.com。有兴趣的读者可以在该主页上获得源代码以进一步参考学习。
 

在此我们讨论的重点并不是这个单体测试工具,而是那个被测试的框架。通过将这个被测试的框架加载到单独的AppDomain,我们可以使其在被测试的过程中成功地读取配置数据。然而问题又来了:如果我们需要在同一次测试中,检验被测框架对不同的配置数据所产生的不同行为,而配置文件却又只能有一个,那么这样的测试需求又如何实现呢?解决该问题的答案就是:我们应该为应用程序框架提供多样化的配置方式,而不仅仅是对配置文件的支持。

设计

在使用.NET技术开发应用程序框架的时候,我们通常会设计一种配置数据结构,在代码中使用继承于ConfigurationSection、ConfigurationElement以及ConfigurationElementCollection的类来表示这样的数据结构,之后,在应用程序的配置文件中,就可以很方便地使用与之相对应的XML标记(Tags)来表示配置数据了。这些内容对于一个资深的.NET开发人员来说,应该是非常熟悉的。可以说,.NET中的配置文件是一种最基本最常见的配置数据提供方案,于是,当我们希望为应用程序框架提供多样化的配置方式时,这种基于配置文件的方式就成为了其中一种必不可少的选择。除此之外,我们还可以根据框架本身的特性,提供诸如基于其它XML文件、基于数据库或者直接代码编写的配置方式。

为了能让框架同时支持配置文件以及其它的配置方式,在设计上就需要将这些不同的配置方式统一起来。上面也分析过,配置文件的方式是必不可少的,因此,我们可以让这些方式对框架透明,而使得框架仅感知到当前只有配置文件这样一种方式。换句话说,在框架的实现过程中,当需要用到配置数据时,框架代码仅会使用到那些继承于ConfigurationSection、ConfigurationElement以及ConfigurationElementCollection的类型。

首先,定义一个配置源(Configuration Source)接口,该配置源接口会对配置节(Configuration Section)进行封装,由于配置节包含了对其它配置元素(Configuration Element)和配置元素集(Configuration Element Collection)的引用,因此,配置节实际上是整个配置信息聚合的聚合根。然后,针对不同的配置方式,创建实现配置源接口

补充:Web开发 , ASP.Net ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,