`
varsoft
  • 浏览: 2430242 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

[原创]WCF后续之旅(7):通过WCF Extension实现和Enterprise Library Unity Container的集成

阅读更多

松耦合、高内聚是我们进行设计的永恒的目标,如何实现这样的目标呢?我们有很多实现的方式和方法,不管这些方式和方法在表现形式上有什么不同,他们的思想都可以表示为:根据稳定性进行关注点的分离或者分解,交互双方依赖于一个稳定的契约,而降低对对方非稳定性因素的依赖。从抽象和稳定性的关系来讲,抽象的程度和稳定程度成正相关关系。由此才有了我们面向抽象编程的说法,所以“只有依赖于不变,才能应万变”。

然后,对于面向对象的思想来讲,我们的功能通过一个个具体的对象来承载。对象是具体的,不是抽象的;创建对象是必然的;对象的创建从某种程度上即使对面向抽象的违背。所以模块之间的耦合度在很大程度上是由于对象创建的方式决定的,而在对象创建过程实现解耦是实现我们“松耦合、高内聚”目标的一个重要途径。对于这一点,我们可以看看我们常用的设计模式中有多少是用于解决如何合理进行对象创建的就可以知道了。Enterprise Library推出的新的Application Block:Unity Application Block为我们提供了一个很好的、可扩展的框架,帮助我们合理、有效的创建对象,并解决创建对象中的依赖。而通过WCF一个简单的扩展对象,就可以很容易地实现和Unity的集成。

1、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方式:

  • Constructor Injection:帮助我们选择我们需要的构造函数来创建对象。
  • Property (Setter) Injection:帮助我们在创建的对象上自动设置某些必要的属性值。
  • Method Injection:帮助我们在对象上调用我们指定的方法做一些初始化的工作。

如果读者想进一步了解Unity Application Block和Enterprise Library,可以访问微软P&P 的网站。

2、实现基于Unity的IntanceProvider

在本系列的第三部分对Dispachter的介绍,和第四部分对WCF可扩展点的介绍中,我提到了一个重要的对象InstanceProvider, 该对象用于service instance的创建。既然Unity的根本目的是创建对象,我们就可以自定义InstanceProvider,让Unity来帮助创建service instance,很容易地实现了和Unity的集成。

下面是我们的自定义InstanceProvider:UnityInstanceProvider

namespace Artech.WCFExtensions
{
public class UnityInstanceProvider: IInstanceProvider
{
private Type _contractType;
private string _containerName;

public UnityInstanceProvider(Type contractType, string containerName)
{
if (contractType == null)
{
throw new ArgumentNullException("contractType");
}

this._containerName = containerName;
this._contractType = contractType;
}

#region IInstanceProvider Members

public object GetInstance(InstanceContext instanceContext, Message message)
{
UnityConfigurationSection unitySection = ConfigurationManager.GetSection("unity") as UnityConfigurationSection;
if (unitySection == null)
{
throw new ConfigurationErrorsException(string.Format(CultureInfo.CurrentCulture, Resources.MissUnityConfiguration));
}

IUnityContainer container = new UnityContainer();
UnityContainerElement containerElement;
if (string.IsNullOrEmpty(this._containerName))
{
containerElement = unitySection.Containers.Default;
}
else
{
containerElement = unitySection.Containers[this._containerName];
}
containerElement.Configure(container);
UnityTypeElement[] unityTypeElements = Array.CreateInstance(typeof(UnityTypeElement), containerElement.Types.Count) as UnityTypeElement[];
containerElement.Types.CopyTo(unityTypeElements, 0);

if (unityTypeElements.Where(element => element.Type == this._contractType).Count() == 0)
{
container.RegisterType(this._contractType, instanceContext.Host.Description.ServiceType);
}

return container.Resolve(this._contractType);
}

public object GetInstance(InstanceContext instanceContext)
{
return this.GetInstance(instanceContext, null);
}

public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
IDisposable disposable = instance as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}

#endregion
}
}

我们来简单讨论一下上面的逻辑。首先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。

private Type _contractType;
private string _containerName;

GetInstance方式提供了service instance创建的实现。具体是这样做的:

UnityConfigurationSection unitySection = ConfigurationManager.GetSection("unity") as UnityConfigurationSection;
if (unitySection == null)
{
throw new ConfigurationErrorsException(string.Format(CultureInfo.CurrentCulture, Resources.MissUnityConfiguration));
}
在配置文件中找到unity的配置,如何没有找到抛出ConfigurationErrorsException异常。

IUnityContainer container = new UnityContainer();
UnityContainerElement containerElement;
if (string.IsNullOrEmpty(this._containerName))
{
containerElement = unitySection.Containers.Default;
}
else
{
containerElement = unitySection.Containers[this._containerName];
}
containerElement.Configure(container);

然后创建UnityContainer对象,然后通过配置的信息对我们创建的UnityContainer进行配置。如何我们没有制定container name,使用默认的配置节,否则使用container name制定的配置节。

UnityTypeElement[] unityTypeElements = Array.CreateInstance(typeof(UnityTypeElement), containerElement.Types.Count) as UnityTypeElement[];
containerElement.Types.CopyTo(unityTypeElements, 0);

if (unityTypeElements.Where(element => element.Type == this._contractType).Count() == 0)
{
container.RegisterType(this._contractType, instanceContext.Host.Description.ServiceType);
}

因为很有可能在unity的配置中,并没有ServiceContract type对应的配置项,在这种情况下,我将自动注册ServiceContract 和ServiceType的匹配关系,ServiceType通过instanceContext.Host.Description.ServiceType获得。

最后通过UnityContainer的Resolve创建service instance。

return container.Resolve(this._contractType);

3、创建UnityInstanceProvider对应的Behavior

InstanceProvider可以通过ContractBehavior来指定,也可以通过EndpointBehavior来指定,我们首先创建ContractBehavior:UnityBehaviorAttribute。由于ContractBehavior通过Custom Attribute的形式指定,所以UnityBehaviorAttribute是一个attribute.

namespace Artech.WCFExtensions
{
public class UnityBehaviorAttribute:Attribute, IContractBehavior
{
public string ContainerName
{ get; set; }

#region IContractBehavior Members

public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{}

public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{}

public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
dispatchRuntime.InstanceProvider = new UnityInstanceProvider(contractDescription.ContractType, this.ContainerName);
}

public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{}

#endregion
}
}

需要做的仅仅是在ApplyDispatchBehavior中,将当前的DispatchRuntime的InstanceProvider 指定成我们的UnityInstanceProvider。

我们再定义一下EndpointBehavior:UnityBehavior。

namespace Artech.WCFExtensions
{
public class UnityBehavior: IEndpointBehavior
{
private string _containerName;

public UnityBehavior(string containerName)
{
this._containerName = containerName;
}

#region IEndpointBehavior Members

public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{}

public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{}

public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.InstanceProvider = new UnityInstanceProvider(endpoint.Contract.ContractType, this._containerName);
}

public void Validate(ServiceEndpoint endpoint)
{}

#endregion
}
}

在ApplyDispatchBehavior,通过endpointDispatcher.DispatchRuntime获得当前的DispatchRuntime对象,并将InstanceProvider我们的UnityInstanceProvider。

最后为了UnityBehavior定义BehaviorExtensionElement:

namespace Artech.WCFExtensions
{
public class UnityBehaviorElement: BehaviorExtensionElement
{
[ConfigurationProperty("containerName", IsRequired = false, DefaultValue = "")]
public string ContainerName
{
get
{
return this["containerName"] as string;
}
set
{
this["containerName"] = value;
}
}

public override Type BehaviorType
{
get
{
return typeof(UnityBehavior);
}
}

protected override object CreateBehavior()
{
return new UnityBehavior(this.ContainerName);
}
}
}

添加一个ContainerName的配置项,在配置文件中指定。

4、应用我们的UnityInstanceProvider

我们现在将我们上面所做的所有工作应用到具体的WCF调用场景中。为此我们创建了一个MessageService的例子(根据Message的Key的Culture返回具体的message的内容),这个例子在本系列第五部分中介绍通过WCF extension实现Localization中介绍过。

这是我们经典的四层结构:

wcf_02_07_01

I、Artech.Messages.Contract:

namespace Artech.Messages.Contract
{
[ServiceContract]
[UnityBehavior(ContainerName ="wcfservice")]
public interface IMessage
{
[OperationContract]
string GetMessage(string key);
}
}

在IMessage上应用了我们的ContractBehavor:UnityBehavior,同时指定container name为:wcfservice。

II、Artech.Messages.Service

namespace Artech.Messages.Service
{
public class MessageService:IMessage
{
[Dependency]
public IMessageManager MessageManager
{ get; set; }

[InjectionMethod]
public void Initialize()
{
Thread.CurrentThread.CurrentCulture = new CultureInfo("zh-CN");
Thread.CurrentThread.CurrentUICulture = new CultureInfo("zh-CN");
}

#region IMessage Members

public string GetMessage(string key)
{
return this.MessageManager.GetMessage(key, Thread.CurrentThread.CurrentUICulture);
}

#endregion
}

public interface IMessageManager
{
string GetMessage(string key, CultureInfo culture, params object[] parameters);
}

public class MessageManager : IMessageManager
{
public ResourceManager ResourceManager
{ get; set; }

public MessageManager()
{
this.ResourceManager =new ResourceManager("Artech.Messages.Service.Properties.Resources", typeof(Resources).Assembly);
}

#region IMessageManager Members

public string GetMessage(string key, CultureInfo culture, params object[] parameters)
{
string message = ResourceManager.GetString(key, culture);
return string.Format(culture, message, parameters);
}

#endregion
}
}

对于MessageService,需要着重介绍一下。因为使用到了一些Unity的Attribute。首先是MessageManager属性,它是一个Interface,上面标注了[Dependency],表明这是一个依赖属性。仔细看代码,此MessageManager并没有进行赋值的地方,而且此属性直接用在了GetMessage()方法上。实际上,对MessageManager进行初始化就是Unity container为我们实现的,在创建MessageService对象后,Unity container会来本container的范围了找到IMessageManager 找到与之复配的concrete type。

[Dependency]
public IMessageManager MessageManager
{ get; set; }

对于我们创建出来的MessageService的对象,我们希望能够自动调用一些初始化的方法来进行一些初始化的工作,我们可以通过InjectionMethodAttribute来实现。在Initialize,我希望指定当前的culture为简体中文(我当前机器默认为en-US)

[InjectionMethod]
public void Initialize()
{
Thread.CurrentThread.CurrentCulture = new CultureInfo("zh-CN");
Thread.CurrentThread.CurrentUICulture = new CultureInfo("zh-CN");
}

而MessageManager 实现了IMessageManager ,提供了GetMessage的真正实现。Message存储在Resource温家中:

Default:

wcf_02_07_02

zh-CN:

wcf_02_07_03

III、Artech.Messages.Hosting

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</configSections>

<system.serviceModel>
<services>
<service name="Artech.Messages.Service.MessageService">
<endpoint behaviorConfiguration="" binding="basicHttpBinding"
contract="Artech.Messages.Contract.IMessage" />
<host>
<baseAddresses>
<add baseAddress="http://127.0.0.1/messageservice" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>

<unity>
<containers>
<container name="wcfservice">
<types>
<type type="Artech.Messages.Service.IMessageManager,Artech.Messages.Service" mapTo="Artech.Messages.Service.MessageManager,Artech.Messages.Service" />
</types>
</container>
</containers>
</unity>
</configuration>

在unity 配置节中,定义了名为wcfservice的container,该名称就是在ServiceContract在UnitBehavior中指定的参数。在该container中定了了IMessageManager和具体的type(MessageManager)之间的匹配。这解决了MessageService中MessageManager属性的实例化的问题。

IV、Artech.Messages.Client

namespace Artech.Messages.Client
{
class Program
{
static void Main(string[] args)
{
using (ChannelFactory<IMessage> channelFactory = new ChannelFactory<IMessage>("messageservice"))
{
IMessage messageProxy = channelFactory.CreateChannel();
Console.WriteLine(messageProxy.GetMessage("HelloWorld"));
}

Console.Read();
}
}
}

messageservice是配置的endpoint的名称。config文件就不列出来了。最后我们运行程序看最终的结果:

wcf_02_07_04

V、使用EndpointBehavior

上面我们是通过ContractBeahvior。现在我们将ServiceContract的UnityBehaviorAttribute去掉,在config中运用我们的EndpointBehavior:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</configSections>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="UnityBehavior">
<UnityBehaviorExtension containerName="wcfservice" />
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="UnityBehaviorExtension" type="Artech.WCFExtensions.UnityBehaviorElement, Artech.WCFExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
<services>
<service name="Artech.Messages.Service.MessageService">
<endpoint behaviorConfiguration="UnityBehavior" binding="basicHttpBinding"
contract="Artech.Messages.Contract.IMessage" />
<host>
<baseAddresses>
<add baseAddress="http://127.0.0.1/messageservice" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
<unity>
<containers>
<container name="wcfservice">
<types>
<type type="Artech.Messages.Service.IMessageManager,Artech.Messages.Service" mapTo="Artech.Messages.Service.MessageManager,Artech.Messages.Service" />
</types>
</container>
</containers>
</unity>
</configuration>

我们一样可以得到上面的结果。

WCF后续之旅:
[原创]WCF后续之旅(1): WCF是如何通过Binding进行通信的
[原创]WCF后续之旅(2): 如何对Channel Layer进行扩展——创建自定义Channel
[原创]WCF后续之旅(3): WCF Service Mode Layer 的中枢—Dispatcher
[原创]WCF后续之旅(4):WCF Extension Point 概览
[原创]WCF后续之旅(5): 通过WCF Extension实现Localization
[原创]WCF后续之旅(6): 通过WCF Extension实现Context信息的传递
[原创]WCF后续之旅(7):通过WCF Extension实现和Enterprise Library Unity Container的集成
[原创]WCF后续之旅(8):通过WCF Extension 实现与MS Enterprise Library Policy Injection Application Block 的集成
[原创]WCF后续之旅(9):通过WCF的双向通信实现Session管理[Part I]
[原创]WCF后续之旅(9): 通过WCF双向通信实现Session管理[Part II]
[原创]WCF后续之旅(10): 通过WCF Extension实现以对象池的方式创建Service Instance

我的WCF之旅:
[原创]我的WCF之旅(1):创建一个简单的WCF程序
[原创]我的WCF之旅(2):Endpoint Overview
[原创]我的WCF之旅(3):在WCF中实现双向通信(Bi-directional Communication)
[原创]我的WCF之旅(4):WCF中的序列化(Serialization)- Part I
[原创]我的WCF之旅(4):WCF中的序列化(Serialization)- Part II
[原创]我的WCF之旅(5):Service Contract中的重载(Overloading)
[原创]我的WCF之旅(6):在Winform Application中调用Duplex Service出现TimeoutException的原因和解决方案
[原创]我的WCF之旅(7):面向服务架构(SOA)和面向对象编程(OOP)的结合——如何实现Service Contract的继承
[原创]我的WCF之旅(8):WCF中的Session和Instancing Management
[原创]我的WCF之旅(9):如何在WCF中使用tcpTrace来进行Soap Trace
[原创]我的WCF之旅(10): 如何在WCF进行Exception Handling
[原创]我的WCF之旅(11):再谈WCF的双向通讯-基于Http的双向通讯 V.S. 基于TCP的双向通讯
[原创]我的WCF之旅(12):使用MSMQ进行Reliable Messaging
[原创]我的WCF之旅(13):创建基于MSMQ的Responsive Service

posted on 2008-07-08 09:03 Artech 阅读(1758) 评论(35) 编辑 收藏

分享到:
评论

相关推荐

    自动化 WCF 集成

    客户端:由MVC担任,且实现自动化服务调用,其中集成了EnterpriseLibrary Ioc。Aop 以及异常管理,缓存管理,且与服务端雷同,可以自行配置 如有不明白之处可以参看微软EnterpriseLibrary,或者度娘,或者自行研究...

    WCF之旅:一个简单的WCF程序(vs2010源码)

    WCF之旅:一个简单的WCF程序(vs2010源码) 文章 + 源码 入门首选文章,折腾了好久才折腾出第一个wcf程序。 对准备学习wcf的人员绝对有意义

    我的WCF之旅:计算器 WCF样例源代码

    我的WCF之旅- 创建一个简单的WCF程序 - Artech WCF入门之选绝佳的例子 代源源于:《WCF全面解析 上》 编程工具:VS2010 语言:C# blog 《IIS站点中部署 WCF项目》

    Apress Pro WCF 4

    ■Chapter 7: Implementing WCF Security ■Chapter 8: Implementing Reliable Messaging and Queue-Based Communications ■Chapter 9: Using Transactions in WCF ■Chapter 10: Integrating with COM+ ■...

    我的WCF之旅源代码_创建一个简单的WCF程序

    我的WCF之旅源代码_创建一个简单的WCF程序

    我的WCF之旅源代码 IIS寄宿

    我的WCF之旅(1)配套代码,IIS寄宿 泛型 序列化

    WCF 我的WCF之旅示例代码

    在学习WCF之旅的时候自己写得一些代码,不同的版本展示了逐渐深入的过程,有文字说明,很经典。

    WCF服务中如何配置Microsoft.Practices.EnterpriseLibrary

    文章:http://blog.csdn.net/yysyangyangyangshan/article/details/12968797

    我的WCF之旅后续篇

    我的WCF之旅后续篇,呀,要大于20字符啊,废话一下

    WCF 学习之旅

    本课件包含课件,及对应源部分,为本人所做课件,可以对照文档一步步实现代码,本课件对WebService 进行了简单的介绍,WCF简单的应用 ,可以布置到各种寄宿平台上,

    WCF之旅 WCF学习的相关资料

    WCF之旅 1. 创建一个简单的WCF程序 3.在WCF中实现双向通信(Bi-directional Communication) 5. 通过WCF Extension实现Localization ......

    WCF开发实战

    WCF开发实战系列一:创建第一个WCF服务 WCF开发实战系列二:使用IIS发布WCF服务 WCF开发实战系列三:自运行WCF服务 WCF开发实战系列四:使用Windows服务发布WCF服务 WCF开发实战系列五:创建WCF客户端程序

    WCF实现聊天通信程序

    利用Wcf实现聊天程序,WCF很好的学习资料

    WCF全面解析(下册)

    如果读者具备了DCOM、Enterprise Library Service、.NET Remoting、Web Service、MSMQ及SOA相关的基础,对阅读此书尽快掌握WCF将大有裨益。 名人推荐我经历了COM时代,一直把Don Box的《COMM本质论》奉为我的指路...

    我的WCF之旅(合集)

    我把WCF之旅制作了个电子书,为WCF做点贡献吧

    我的wcf之旅1-13

    Artech的博客文章,我把它转成chm的格式给大家分享

    自动加载 WCF Library Host示例代码

    如何做一个主程序,主程序里没有任何契约和配置文件能自动Host指定的外部的WCF Library Assembly。 http://blog.csdn.net/fangxinggood/article/details/7107597

    WCF入门和提高:WCF足迹

    摘自网友博客 非常不错的学习WCF的文章

    WCF技术剖析,wcf教程+.pdf

    蒋金楠老师的wcf教程,个人看了,觉得写的很不错,对刚入门和深入了解wcf都适用,有需要的同学可下载学习

    WCFService可以通过web调用和WCF服务访问

    WCF服务,配置可同时支持web调用和WCF调用,可以用于C#程序服务访问,也支持java等程序访问。java通过web调用,C#可以通过wcf访问。

Global site tag (gtag.js) - Google Analytics