微软企业库4.1学习笔记(十二)中间人Providers,设计时支持Design-Time Configuration和仪表盘Instrumentation
一、Provider
在.NET frameword中每一个provider都是一段代码的中间人,通过它,你的应用可以连接到一个服务或者是数据源,可以获取或者是修改服务和数据源的数据。在企业库中包含很多的providers。另外,你也可以创建自己的provider,在你的应用中满足特殊的需求。
一个provider type定义了和一个模块相关的功能的接口,一个provider是一个provider type的实现。每一个模块中,对于一个provider type,都实现了很多的provider。也可以为模块添加自定义的provider。
通过特殊的实现来分离模块的功能,可以达到下面的目标:
- 变化性,这样可以根据应用的需求,解耦相同功能的多个实现。
- 扩展性,允许你在某些强制实现的情况下使用模块。例如,应用在特殊的环境中,要求满足一种特殊的加密算法。
- 封装性,使用provider,功能不再是模块的一部分,可以被代替和升级,而不对模块的其他部分有影响。
- 跨环境的轻便型,你可以在新环境中部署模块,使用为这个环境编写的provider。你也可以编写一个provider,运行在一个环境中,却模拟另外一个环境。
- 使得模块之间的联系最小化,如果一个模块依赖于另一个模块,可以将这种依赖封装在provider中,一个模块更新之后,如果使用provider,它依赖的模块不用更新。例如,异常处理模块,依赖于日志模块来记录异常信息。一个新版本的日志模块需要一个新的日志provider,但是异常处理模块可以不用更新。
二、设计时配置
所有的模块对于配置节中都包括运行时支持和设计时支持。运行时支持包括配置中的类,在模块加载的时候使用配置中的类。从配置中读取信息,给模块返回包含在配置中的对象。
设计时支持包含一些类,这些类使你可以用配置工具改变配置信息。
下面的图中可以看出运行时支持和设计时支持的关系
简单的来所运行时支持的类,使得你可以在运行的时候利用配置节中的信息动态创建对象或者是其他的功能;设计时支持的类,使得你可以在设计的时候使用可视化的界面来设置配置节中的信息,不用手写xml配置节了。
设计时的类依赖于运行时的类,因为通过工具修改配置之后,需要保存,这时候就需要调用运行时的类将配置保存起来。运行时的类不依赖于设计时的类。每一个模块的设计时支持用的类都是单独的程序集。例如,Microsoft.Practices.EnterpriseLibrary.Security.Cache.Configuration.Design.dll,都是包含.Design的。这些程序集在你的应用运行的时候不需要他们,但是在你使用配置工具的时候就需要他们了。
三、仪表盘
在企业库中,激发事件的代码,和激发事件之后指定应该发生那些行为的代码是分开的。激发事件的代码和provider有关系,对事件作出响应的代码则和listener代码有关。这种分离允许你在事件发生的时候改变行为,而不用重新编译provider代码。但是,重新编译listener还是需要的。
在你感兴趣的东西发生的时候会激活一些事件,例如连接数据库,记录日志。激活事件会在代码运行的时候利用反射调用相关的listener。listener决定了在发生事件的时候,会有那些行为,例如,写日志的时候,可以记录写日志事件,或者是增加计数器。没有listener的模块也可以正常运行。
3.1使用特性Attribute
你可以用下面的特性,来给应用添加listener和instrument
- InstrumentationListener
- InstrumentationProvider
- InstrumentationConsumer
InstrumentationListener特性出现在provider类的上面,告诉它初始化了那一个listener。InstrumentationProvider特性用在provider类的事件上面。InstrumentationConsumer特性用在listener类的事件处理方法上面,标识的名称必须和InstrumentationProvider标识的名称相同。
下面是一段示例代码
public class MyListener { public MyListener (string instanceName,bool a,bool b,bool c){} [InstrumentationConsumerAttribute ("DbConnect")] public void ConnectObserved(object sender,EventArgs e) { Console .WriteLine ("I saw a database connect."); } } [InstrumentationListener (typeof (MyListener ))] public class MyApplication { [InstrumentationProvider("DbConnect")] public event EventHandler <EventArgs > OnDbConnect; }
MyApplication就是一个provider,OnDbConnect是provider的一个事件,MyListener中的ConnectObserved就是响应OnDbConnect事件的处理代码。
当系统初始化MyApplication类的时候,会检查特性中是否存在一个MyListener类型的listener,如果存在,也会初始化一个MyListener。然后会检查MyApplication中所有的事件,查看那些被标识了InstrumentationProvider特性。同时也会在MyListener中查看标有InstrumentationConsumer特性的,相同名称的方法。
3.2为组件提供仪表功能
一个应用会包含很多的组件,例如数据库组件。在这时候,可能需要仪表的不是应用本身,而是这个组件。为组件实现仪表功能,要求你的应用实现IInstrumentationEventProvider接口。下面的示例中,MyApplication就是一个应用。
public class MyListener { public MyListener (string instanceName,bool a,bool b,bool c){} [InstrumentationConsumerAttribute ("DbConnect")] public void ConnectObserved(object sender,EventArgs e) { Console .WriteLine ("I saw a database connect."); } } public class MyApplication:IInstrumentationEventProvider { private MyInstrumentationProvider instrumentationProvider= new MyInstrumentationProvider (); public object GetInstrumentationEventProvider() { return instrumentationProvider ; } } [InstrumentationListener (typeof (MyListener ))] public class MyInstrumentationProvider { [InstrumentationProvider("DbConnect")] public event EventHandler <EventArgs> OnDbConnect; public bool IsWired { get{return OnDbConnect !=null;} } }
通常系统会从MyApplication类的第一行开始查找InstrumentationListener特性。但是,在上面的例子中,没有这个特性。因此,当系统看到IInstrumentationEventProvider接口的时候,会执行GetInstrumentationEventProvider方法,查找方法返回类型的InstrumentationListener特性,然后查找InstrumentationProvider特性。
3.3安装仪表盘功能
在listener类中的仪表盘会添加HasInstallableResources特性。下面是一段代码示例。
[HasInstallableResources ] [PerformanceCountersDefinitionAttribute ("Enterprise Library Data Counters", "CounterCategoryHelpResourceName")] public class DataInstrumentationListener:InstrumentationListener { [PerformanceCounter ("Connections Opend/sec","ConnectionOpenedCounterHelpResource", System.Diagnostics.PerformanceCounterType .RateOfCountsPerSecond32 )] EnterpriseLibraryPerformanceCounter connectionOpenedCounter; }