如何用C#和.NET Framework开发自己的插件构架

在企业环境中,允许在内部应用程序中编写并导入插件,而不必改变核心应用的情况十分少见。我认为原因之一在于:以前很难执行这类使用插件的构架。

开发者被迫花大量时间设计构架,或开发替代方案完成其目标。我们都知道,在企业环境中不容易获得大量时间,所以大多数开发者似乎都被迫选择开发替代方案。微软已经用C#.NET Framework解决这个问题。因此现在开发一款允许动态插入组件,且不必改变核心代码的应用程序已不再重要。

本文中包括的解决方案(参见下载的项目文件)说明如何执行一个简单的插件构架。这个示范解决方案动态执行测试(每个测试为一个插件)并报告结果。每个测试完成不同的任务,并可以添加或删除测试,而不会影响到核心测试引擎的功能。这样我们就可以给应用程序增加测试,而不必重建整个应用程序。我将这个解决方案称为“SystemTests”,它包含四个项目:SharedObjects、TestEngine、TestExamples和TestApplication

许多允许使用插件的应用程序都有一些激活插件功能的基本层,SystemTests方案也不例外。

共享对象层

共享对象层为主应用程序提供一种与插件通信的方法。主应用程序和插件引用同样的对象。

共享对象(Shared Objects)层由SharedObject项目执行。这个项目包括以下各项:

  • ITest.cs—这是一个控制核心应用程序与插件通信的接口。在这个接口中,您可以发现下列方法与属性:
    ☉方法:
    Execute(TestEngineState) – 执行测试。
    ☉ 属性:
    Result– 返回一个存储测试结果信息的TestResult对象。
    ID—存储测试ID。
    Name—存储测试名称。
    Description—存储测试说明。
  • Test.cs – 这是一个功能很弱的基本类。它是一个辅助类,每个测试由它发展而来。继承这个类后,编写测试的开发者不必明确写出执行ITest接口所需的属性。
  • TestEngineState.cs – 这个类被提交给每个测试,帮助每个测试确定之前的哪个测试通过或失败。使用这个类可以使测试彼此依赖。
  • TestResult.cs – 测试执行完成后,它生成带一个TestResult对象的Result属性。这个类包含几个允许测试引擎决定测试结果的属性:
    ☉ 属性:
    Message – 这个属性存储测试何以通过或失败的信息。
    Passed – 这是一个表明测试是否通过的布尔属性。如果测试通过,此属性为真。

核心或主应用程序层

主应用程序层是应用程序执行核心功能的地方。这个层把所有插件连接起来,完成所需的报告,并与用户或数据库进行互操作。

主应用程序层由TestEngine项目执行,它包含一个名为“Engine”的类。收集必须运行的测试并运行它们,并通过一个TestEngineState项目将结果返回给调用的应用程序,是Engine类的工作。

LoadConfigurationCreateTestEngine类的两个最重要的方法。下面详细说明这两个方法:

  • LoadConfiguration

通过解析一个XML配置文件确定必须加载哪个插件。LoadConfiguration列表A中的代码执行,其中的注释对代码进行了解释:

列表A

privatevoid LoadConfiguration(string path)

{

XmlDocument configuration = newXmlDocument();

//Load the given XML file into our XML document.

configuration.Load(path);

//Get a NodeList of the Test nodes.

XmlNodeList tests = configuration.SelectNodes("/TestEngine/Tests/Test");

//Loop through the test nodes and load each test.

foreach (XmlNode test in tests)

{

//Load the enabled attribute to determine if the test is enabled.

XmlAttribute enabled = test.Attributes["Enabled"];

if (enabled.Value.ToLower() == "true")

{

XmlAttribute assemblyPath = test.Attributes["Assembly"];

XmlAttribute typeName = test.Attributes["Type"];

//Call CreateTest to instantiate the test and add it to the

// Tests property.

this.Tests.Add(CreateTest(assemblyPath.Value, typeName.Value));

}

}

}

  • CreateTest

从指定的装配路径和类型名称中建立一个ITest对象。这个方法如列表B所示,其中的注释说明了从装配中动态初始化一个对象的必要步骤。

列表B

privateITest CreateTest(string assemblyPath, string typeName)

{

//Create an ObjectHandle object that will hold an instance of the requested

// type in the given assembly. The ObjectHandle class is in the

// System.Remoting namespace.

ObjectHandle handle = Activator.CreateInstanceFrom(assemblyPath, typeName);

//Unwrap the object instance into an object class.

object test = handle.Unwrap();

//Test to make sure the object implements the ITest interface. Any object

// that does not implement the ITest interface will cause an exception to

// be thrown.

if (test isITest)

return (ITest)test; //Return the object as an ITest instance.

else

thrownewException("This Type does not implement ITest, which is required.\nAssembly: " + assemblyPath + "\nType: " + typeName);

}

插件层

插件层中包含为应用程序建立的所有插件。一般来说,这个层中包含的插件执行共享对象层中的一个或几个接口。这允许主应用程序层与插件进行通信。

插件层由TestExamples项目执行。每个测试的功能各不相同,对于测试的功能也没有特别规定。唯一的规定就是测试必须执行ITest接口,其它功能由测试开发者决定。

列表C是一个非常简单的名为“MathTest”的测试实例。如果1不等于2(1!=2),则测试通过;如果1等于2(1==2),则测试失败:

列表C

using System;

using System.Collections.Generic;

using System.Text;

using SystemTests.SharedObjects;

namespace SystemTests.TestExamples

{

publicclassMathTest : Test, ITest

{

publicoverridevoid Execute(TestEngineState state)

{

//Set the ID for this test.

this.ID = "MathTest";

//Set the Name of this test.

this.Name = "Math Test";

//Set the description of this test.

this.Description = "Tests to see if 1 = 2";

//Instantiate a TestResult object to hold the

// results of this test.

TestResult result = newTestResult();

//Very simple test to see if 1 = 2

if (1 == 2)

result.Passed = true;

else

result.Passed = false;

//Set the result.

this.Result = result;

}

}

}

虽然这个测试十分简单,如上所述,只要它执行ITest接口,它就能实现任何功能。

本文中的解决方案包含另外两个样本测试:

  • SuperManTest – 决定Superman类是否执行IsuperHero接口。
  • WebTest – 决定本地计算机是否能够访问http://microsoft.com/,(如果您使用代理,这个测试可能失败。)

本文包含的解决方案中还有另外一个名为“TestApplication”的项目。这个项目是一个非常简单的Windows应用程序,它显示TestEngine的结果。

应用插件解决自己的问题

了解本文中包含的C#和.NET Framework解决方案,考虑如何将这种类型的插件功能用于解决您所在公司的商业问题。可能至少有一些情形适用这种高度动态化的功能。在适当的时候使用本文中的代码实例!

posted @ 2006-12-12 09:13  白鸟云平  阅读(961)  评论(0)    收藏  举报