WCF后续之旅(7):通过WCF Extension实现和Enterprise Library Unity Container的集成
松耦合、高内聚是我们进行设计的永恒的目标,如何实现这样的目标呢?我们有很多实现的方式和方法,不管这些方式和方法在表现形式上有什么不同,他们的思想都可以表示为:根据稳定性进行关注点的分离或者分解,交互双方依赖于一个稳定的契约,而降低对对方非稳定性因素的依赖。从抽象和稳定性的关系来讲,抽象的程度和稳定程度成正相关关系。由此才有了我们面向抽象编程的说法,所以“只有依赖于不变,才能应万变”。
然后,对于面向对象的思想来讲,我们的功能通过一个个具体的对象来承载。对象是具体的,不是抽象的;创建对象是必然的;对象的创建从某种程度上即使对面向抽象的违背。所以模块之间的耦合度在很大程度上是由于对象创建的方式决定的,而在对象创建过程实现解耦是实现我们“松耦合、高内聚”目标的一个重要途径。对于这一点,我们可以看看我们常用的设计模式中有多少是用于解决如何合理进行对象创建的就可以知道了。Enterprise Library推出的新的Application Block:Unity Application Block为我们提供了一个很好的、可扩展的框架,帮助我们合理、有效的创建对象,并解决创建对象中的依赖。而通过WCF一个简单的扩展对象,就可以很容易地实现和Unity的集成。
一、Unity Application Block由于本篇文章的重点仍然是对WCF的扩展,因此我不会花太多的篇幅对Enterprise Library Unity作详细的介绍。这是比较官方的定义:"The Unity Application Block (Unity) is a lightweight, extensible dependency injection container with support for constructor, property, and method call injection”. Unity实际是建立在ObjectBuilder基础之上,而ObjectBuilder是整个Enterprise Library的基石(实际上还不止于此,MS P P开发的很多的开源框架都依赖于ObjectBuilder,比如CAB、SCSF等),为我们提供了一个可扩展的、基于策略(strategy based)对象创建方式。借助于ObjectBuilder,Unity可以帮助我们基于Interface或者abstract class创建对象;可以帮助我们管理对象的生命周期;以及实现依赖注入(DI:Dependency Injection)。下面是3种主要的DI方式:
如果读者想进一步了解Unity Application Block和Enterprise Library,可以访问微软P P 的网站。
二、实现基于Unity的IntanceProvider在本系列的第三部分对Dispachter的介绍,和第四部分对WCF可扩展点的介绍中,我提到了一个重要的对象InstanceProvider, 该对象用于service instance的创建。既然Unity的根本目的是创建对象,我们就可以自定义InstanceProvider,让Unity来帮助创建service instance,很容易地实现了和Unity的集成。
下面是我们的自定义InstanceProvider:UnityInstanceProvider
21: public object GetInstance(InstanceContext instanceContext, Message message)
22: {
23: UnityConfigurationSection unitySection = ConfigurationManager.GetSection("unity") as UnityConfigurationSection;
24: if (unitySection == null)
25: {
26: throw new ConfigurationErrorsException(string.Format(CultureInfo.CurrentCulture, Resources.MissUnityConfiguration));
27: }
28:
29: IUnityContainer container = new UnityContainer();
30: UnityContainerElement containerElement;
31: if (string.IsNullOrEmpty(this._containerName))
32: {
33: containerElement = unitySection.Containers.Default;
34: }
35: else
36: {
37: containerElement = unitySection.Containers[this._containerName];
38: }
39: containerElement.Configure(container);
40: UnityTypeElement[] unityTypeElements = Array.CreateInstance(typeof(UnityTypeElement), containerElement.Types.Count) as UnityTypeElement[];
41: containerElement.Types.CopyTo(unityTypeElements, 0);
42:
43: if (unityTypeElements.Where(element = element.Type == this._contractType).Count() == 0)
44: {
45: container.RegisterType(this._contractType, instanceContext.Host.Description.ServiceType);
46: }
47:
48: return container.Resolve(this._contractType);
49: }
50:
51: public object GetInstance(InstanceContext instanceContext)
52: {
53: return this.GetInstance(instanceContext, null);
54: }
55:
56: public void ReleaseInstance(InstanceContext instanceContext, object instance)
57: {
58: IDisposable disposable = instance as IDisposable;
59: if (disposable != null)
60: {
61: disposable.Dispose();
62: }
63: }
64:
65: #endregion
66: }
67: }
68:
我们来简单讨论一下上面的逻辑。首先Unity常用的做法就根据Interface或者abstract class来进行对象的创建,从而实现对抽象的依赖,而Interface或者abstract class和concrete type之间的mapping关系可以通过配置或者代码注册。对于service instace来说,这个Interface就是ServiceContract。而Unity通过一个叫做UnityContainer对象创建具体的对象和进行生命周期的管理,Container是一个囊括了所有对象创建和生命周期管理所需资源的容器。这些资源包括:Interface或者abstract class和concrete type之间的mapping关系;Dependency Injection的定义;Extensions等等。UnityContainer之间可以嵌套从而形成一个树状结构,我们可以通过一个ID来定位我们需要的container。所以我们定义了两个field:_contractType和_containerName。
1: UnityConfigurationSection unitySection = ConfigurationManager.GetSection("unity") as UnityConfigurationSection;
2: if (unitySection == null)
3: {
4: throw new ConfigurationErrorsException(string.Format(CultureInfo.CurrentCulture, Resources.MissUnityConfiguration));
5: }
然后创建UnityContainer对象,然后通过配置的信息对我们创建的UnityContainer进行配置。如何我们没有制定container name,使用默认的配置节,否则使用container name制定的配置节。
1: UnityTypeElement[] unityTypeElements = Array.CreateInstance(typeof(UnityTypeElement), containerElement.Types.Count) as UnityTypeElement[];
2: containerElement.Types.CopyTo(unityTypeElements, 0);
3:
4: if (unityTypeElements.Where(element = element.Type == this._contractType).Count() == 0)
5: {
6: container.RegisterType(this._contractType, instanceContext.Host.Description.ServiceType);
7: }
因为很有可能在unity的配置中,并没有ServiceContract type对应的配置项,在这种情况下,我将自动注册ServiceContract 和ServiceType的匹配关系,ServiceType通过instanceContext.Host.Description.ServiceType获得。
最后通过UnityContainer的Resolve创建service instance。
三、创建UnityInstanceProvider对应的Behavior
InstanceProvider可以通过ContractBehavior来指定,也可以通过EndpointBehavior来指定,我们首先创建ContractBehavior:UnityBehaviorAttribute。由于ContractBehavior通过Custom Attribute的形式指定,所以UnityBehaviorAttribute是一个attribute.
10: public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
11: {}
12:
13: public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
14: {}
15:
16: public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
17: {
18: dispatchRuntime.InstanceProvider = new UnityInstanceProvider(contractDescription.ContractType, this.ContainerName);
19: }
20:
21: public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
22: {}
23:
24: #endregion
25: }
26: }
27:
需要做的仅仅是在ApplyDispatchBehavior中,将当前的DispatchRuntime的InstanceProvider 指定成我们的UnityInstanceProvider。
我们再定义一下EndpointBehavior:UnityBehavior。
14: public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
15: {}
16:
17: public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
18: {}
19:
20: public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
21: {
22: endpointDispatcher.DispatchRuntime.InstanceProvider = new UnityInstanceProvider(endpoint.Contract.ContractType, this._containerName);
23: }
24:
25: public void Validate(ServiceEndpoint endpoint)
26: {}
27:
28: #endregion
29: }
30: }
31:
在ApplyDispatchBehavior,通过endpointDispatcher.DispatchRuntime获得当前的DispatchRuntime对象,并将InstanceProvider我们的UnityInstanceProvider。
最后为了UnityBehavior定义BehaviorExtensionElement:
5: [ConfigurationProperty("containerName", IsRequired = false, DefaultValue = "")]
6: public string ContainerName
7: {
8: get
9: {
10: return this["containerName"] as string;
11: }
12: set
13: {
14: this["containerName"] = value;
15: }
16: }
17:
18: public override Type BehaviorType
19: {
20: get
21: {
22: return typeof(UnityBehavior);
23: }
24: }
25:
26: protected override object CreateBehavior()
27: {
28: return new UnityBehavior(this.ContainerName);
29: }
30: }
31: }
32:
添加一个ContainerName的配置项,在配置文件中指定。
四、应用我们的UnityInstanceProvider我们现在将我们上面所做的所有工作应用到具体的WCF调用场景中。为此我们创建了一个MessageService的例子(根据Message的Key的Culture返回具体的message的内容),这个例子在本系列第五部分中介绍通过WCF extension实现Localization中介绍过。
这是我们经典的四层结构:
I、Artech.Messages.Contract:
在IMessage上应用了我们的ContractBehavor:UnityBehavior,同时指定container name为:wcfservice。
II、Artech.Messages.Service
20: return this.MessageManager.GetMessage(key, Thread.CurrentThread.CurrentUICulture);
21: }
22:
23: #endregion
24: }
25:
26: public interface IMessageManager
27: {
28: string GetMessage(string key, CultureInfo culture, params object[] parameters);
29: }
30:
31: public class MessageManager : IMessageManager
32: {
33: public ResourceManager ResourceManager
34: { get; set; }
35:
36: public MessageManager()
37: {
38: this.ResourceManager =new ResourceManager("Artech.Messages.Service.Properties.Resources", typeof(Resources).Assembly);
39: }
40:
41: #region IMessageManager Members
42:
43: public string GetMessage(string key, CultureInfo culture, params object[] parameters)
44: {
45: string message = ResourceManager.GetString(key, culture);
46: return string.Format(culture, message, parameters);
47: }
48:
49: #endregion
50: }
51: }
52:
对于MessageService,需要着重介绍一下。因为使用到了一些Unity的Attribute。首先是MessageManager属性,它是一个Interface,上面标注了[Dependency],表明这是一个依赖属性。仔细看代码,此MessageManager并没有进行赋值的地方,而且此属性直接用在了GetMessage()方法上。实际上,对MessageManager进行初始化就是Unity container为我们实现的,在创建MessageService对象后,Unity container会来本container的范围了找到IMessageManager 找到与之复配的concrete type。
对于我们创建出来的MessageService的对象,我们希望能够自动调用一些初始化的方法来进行一些初始化的工作,我们可以通过InjectionMethodAttribute来实现。在Initialize,我希望指定当前的culture为简体中文(我当前机器默认为en-US)
而MessageManager 实现了IMessageManager ,提供了GetMessage的真正实现。Message存储在Resource温家中:
Default:
zh-CN:
III、Artech.Messages.Hosting
4: section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/
5: /configSections
6:
7: system.serviceModel
8: services
9: service name="Artech.Messages.Service.MessageService"
10: endpoint behaviorConfiguration="" binding="basicHttpBinding"
11: contract="Artech.Messages.Contract.IMessage" /
12: host
13: baseAddresses
14: add baseAddress="http://127.0.0.1/messageservice" /
15: /baseAddresses
16: /host
17: /service
18: /services
19: /system.serviceModel
20:
21: unity
22: containers
23: container name="wcfservice"
24: types
25: type type="Artech.Messages.Service.IMessageManager,Artech.Messages.Service" mapTo="Artech.Messages.Service.MessageManager,Artech.Messages.Service" /
26: /types
27: /container
28: /containers
29: /unity
30: /configuration
31:
在unity 配置节中,定义了名为wcfservice的container,该名称就是在ServiceContract在UnitBehavior中指定的参数。在该container中定了了IMessageManager和具体的type(MessageManager)之间的匹配。这解决了MessageService中MessageManager属性的实例化的问题。
IV、Artech.Messages.Client
7: using (ChannelFactory IMessage channelFactory = new ChannelFactory IMessage ("messageservice"))
8: {
9: IMessage messageProxy = channelFactory.CreateChannel();
10: Console.WriteLine(messageProxy.GetMessage("HelloWorld"));
11: }
12:
13: Console.Read();
14: }
15: }
16: }
17:
messageservice是配置的endpoint的名称。config文件就不列出来了。最后我们运行程序看最终的结果:
V、使用EndpointBehavior
上面我们是通过ContractBeahvior。现在我们将ServiceContract的UnityBehaviorAttribute去掉,在config中运用我们的EndpointBehavior:
4: section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/
5: /configSections
6: system.serviceModel
7: behaviors
8: endpointBehaviors
9: behavior name="UnityBehavior"
10: UnityBehaviorExtension containerName="wcfservice" /
11: /behavior
12: /endpointBehaviors
13: /behaviors
14: extensions
15: behaviorExtensions
16: add name="UnityBehaviorExtension" type="Artech.WCFExtensions.UnityBehaviorElement, Artech.WCFExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /
17: /behaviorExtensions
18: /extensions
19: services
20: service name="Artech.Messages.Service.MessageService"
21: endpoint behaviorConfiguration="UnityBehavior" binding="basicHttpBinding"
22: contract="Artech.Messages.Contract.IMessage" /
23: host
24: baseAddresses
25: add baseAddress="http://127.0.0.1/messageservice" /
26: /baseAddresses
27: /host
28: /service
29: /services
30: /system.serviceModel
31: unity
32: containers
33: container name="wcfservice"
34: types
35: type type="Artech.Messages.Service.IMessageManager,Artech.Messages.Service" mapTo="Artech.Messages.Service.MessageManager,Artech.Messages.Service" /
36: /types
37: /container
38: /containers
39: /unity
40: /configuration
41:
工作笔记 | Visual Studio 调用 Web Service 最近笔者负责ERP财务系统跟中粮集团财务公司的财务系统做对接,鉴于ERP系统中应付结算单结算量比较大,而且管理相对集中,ERP系统与中粮财务公司的支付平台系统对接,实现银企直联,将网银录入的环节、付款以后ERP确认环节自动化,节省人工操作环节带来的误差。
WPF 多语言解决方案 - Multilingual App Toolkit 原文:WPF 多语言解决方案 - Multilingual App Toolkit 1、首先安装Multilingual App Toolkit 2、新建项目,在VS中点击 工具 - Multilingual App Toolkit - 启用选定内容 如果出现上述Issue, 打开项目AssemblyInfo.
[UWP]如何使用Fluent Design System (上) 原文:[UWP]如何使用Fluent Design System (上) 1. 前言 微软在Build 2017中公布了新的设计语言Fluent Design System(以下简称FDS),不过官网只是堆砌了各种华丽的词语以及一堆动画。
[UWP]如何使用Fluent Design System (下) 原文:[UWP]如何使用Fluent Design System (下) 4. 兼容旧版本 FDS最常见的问题之一是如何与Fall Creators Update之前的版本兼容,其实做起来也挺简单的,ColorfulBox就实现了Creators Update与Fall Creators Update之间的兼容。
《Visual Edge Computing Service视图计算产品发布介绍》PDF 立即下载
相关文章
- WCF学习之旅—基于ServiceDebug的异常处理(十七)
- WCF学习之旅—WCF寄宿前的准备(八)
- WCF学习之旅—WCF第二个示例(六)
- 动态的调用服务端的WCF中的方法
- WCF的配置文件中的要素
- Learning WCF Chapter1 Creating a New Service from Scratch
- WCF入门教程(四)通过Host代码方式来承载服务 一个WCF使用TCP协议进行通协的例子 jquery ajax调用WCF,采用System.ServiceModel.WebHttpBinding System.ServiceModel.WSHttpBinding协议 学习WCF笔记之二 无废话WCF入门教程一[什么是WCF]
- C# -- HttpWebRequest 和 HttpWebResponse 的使用 C#编写扫雷游戏 使用IIS调试ASP.NET网站程序 WCF入门教程 ASP.Net Core开发(踩坑)指南 ASP.Net Core Razor+AdminLTE 小试牛刀 webservice创建、部署和调用 .net接收post请求并把数据转为字典格式
- 大比速:remoting、WCF(http)、WCF(tcp)、WCF(RESTful)、asp.net core(RESTful) .net core 控制台程序使用依赖注入(Autofac)
- WCF系列
- Silverlight+WCF 新手实例 象棋 介绍(一)
- 动态调用WCF不添加服务(svcutil.exe)
- Silverlight+WCF 实战-网络象棋最终篇之对战视频-下篇[客户端发送与服务端中转](六)
- WCF系列(六) - WCF安全系列(一) - basicHttpBinding
- WCF系列教程之WCF客户端异常处理
- WCF系列教程之初识WCF
- WCF-how to write a WCF client for a JSON REST service