程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> WCF技術剖析之七:如何實現WCF與EnterLib PIAB、Unity之間的集成

WCF技術剖析之七:如何實現WCF與EnterLib PIAB、Unity之間的集成

編輯:關於.NET

在這之前,我寫過深入介紹MS EnterLib PIAB的文章(參閱《MS Enterprise Library Policy Injection Application Block 深入解析[總結篇]》),也寫過WCF與PIAB的集成(參閱:《WCF後續之旅(8):通過WCF Extension 實現與MS Enterprise Library Policy Injection Application Block 的集成》)、WCF與Unity的集成(參閱《WCF後續之旅(7):通過WCF Extension實現和Enterprise Library Unity Container的集成》)以及Unity與PIAB的集成(參閱《Enterprise Library深入解析與靈活應用(1):通過Unity Extension實現和Policy Injection Application Block的集成》、《Enterprise Library深入解析與靈活應用(7):再談PIAB與Unity之間的集成》)。由於部分實現時基於EnterLib、Unity前一個版本,在新的版本中(EnterLib V4.1與Unity 1.2)中,MS通過Unity對PIAB進行了重新設計與實現,所以我們很有必要重拾著這個話題,談談對於新的EnterLib和Unity,如何將PIAB和Unity集成到WCF之中。

一、設計原理簡述

在EnterLib中,PIAB與Unity的定位分別是輕量級的IoC Container(或者DI Container)與AOP框架。PIAB采用Method Call Interception的機制實現了策略的動態注入,其本身依賴於Interceptable對象的創建;UnityContainer建立在ObjectBuilder2之上,本質上是一個用於對象創建的容器。所以,我們可以通過UnityContainer按照PIAB的要求創建Interceptable對象,就能實現Unity與PIAB之間的集成(參閱《Enterprise Library深入解析與靈活應用(7):再談PIAB與Unity之間的集成》)。

Unity與WCF之間的集成,本質上就是讓WCF使用UnityContainer進行服務實例的創建。而WCF框架內部,服務實例的創建同時一個特殊的對象——InstanceProvider。所以我們可以通過自定義InstanceProvider,並借助UnityContainer進行服務實例的提供,那麼就能實現Unity與WCF兩者之間的集成。所以,創建基於UnityContainer的InstanceProvider是關鍵。

二、創建基於UnityContainer的InstanceProvider:UnityInstanceProvider

在WCF框架內部,InstanceProvider用戶進行服務實例的提供。所有的InstanceProvider實現了接口System.ServiceModel.Dispatcher.IInstanceProvider,下面是IInstanceProvider的定義。服務實例提供實現在GetInstance中,而ReleaseInstance用於實現對服務實例的釋放和資源回收。

1: public interface IInstanceProvider
2: {
4: object GetInstance(InstanceContext instanceContext);
5: object GetInstance(InstanceContext instanceContext, Message message);
6: void ReleaseInstance(InstanceContext instanceContext, object instance);
7: }

我們現在的目的就是創建一個基於Unity的InstanceProvider,借助UnityContainer提供對GetInstance方法的實現,我姑且把這個自定義的InstanceProvider稱為UnityInstanceProvider。在正式介紹UnityInstanceProvider的具體實現之前,我先介紹一個輔助類型的定義:UnityTypeMapping。

我們知道,UnityContainer采用動態注冊接口或者抽象類於具體類型的匹配關系,使得我們可以利用UnityContaner實現基於接口或者抽象類的方式創建我們希望的具體類的對象。UnityTypeMapping用以描述類型的匹配,其定義如下。Type和Mapto分別表示相應的類型(接口或者抽象類)與被匹配的類型(具體類),Name則表示該Mapping Entry的名稱(唯一標識)。

1: public class UnityTypeMapping
2: {
3: public Type Type
4: { get; set; }
5:
6: public Type MapTo
7: { get; set; }
8:
9: public string Name
0: { get; set; }
1: }

Unity可以采用編程和配置的方式實現類型的匹配,在真正的系統開發中,後者是首選。為了實現類型匹配配置(UnityTypeElementCollection)到我們定義的UnityTypeMapping列表(IList<UnityTypeMapping>)之間的轉化,我定義了下面一個擴展方法(Extension Method):Copy。

1: public static class Extension
2: {
3: public static IList<UnityTypeMapping> Copy(this UnityTypeElementCollection unityTypeElements)
4: {
5: IList<UnityTypeMapping> mappings = new List<UnityTypeMapping>();
6: foreach (UnityTypeElement type in unityTypeElements)
7: {
8: mappings.Add(new UnityTypeMapping { Type = type.Type, MapTo = type.MapTo, Name = type.Name });
9: }
0:
1: return mappings;
2: }
3: }

下面列出了UnityInstanceProvider的具體定義。屬性ContractType與Container分別代表服務契約與用於創建服務實例的UnityContainer對象,字段_registeredTypeMapping表示當前UnityContainer相關的類型匹配集合。出於性能的考慮,為了避免UnityContainer的頻繁創建和類型匹配關系的頻繁解析,我通過兩個靜態屬性|字段來保存它們(Containers和registeredTypeMappings,Key為Container的名稱)。在構造函數中接受兩個輸入參數:contractType與containerName,分別表示服務契約類型與相應UnityContainer的名稱。根據containerName判斷相應的UnityContainer是否已經創建,如果是,則直接從上述的兩個靜態變量中提取相應的UnityContainer和類型匹配列表。否則,重新創建UnityContainer,加載相應的配置信息對其進行配置。需要特別指出的是,在對創建的UnityContainer進行初始化的時候,添加了一個特殊的UnityContainerExtension:ExtendedIntercepiton,該UnityContainerExtension用戶實現Unity與PIAB的集成,在《Enterprise Library深入解析與靈活應用(7):再談PIAB與Unity之間的集成》中對ExtendedIntercepiton的實現原理具有詳細的介紹。

在GetInstance方法中,我們通過UnityContainer根據服務契約(接口)類新進行具體服務實例的創建。在創建之前,我們需要判斷服務契約類型與服務類型之間的類型匹配是否已經注冊到UnityContainer中,如果沒有,則進行注冊,並將類型匹配添加到當前類型匹配列表(_registeredTypeMappings)和全局類型匹配列表(registeredTypeMappings)中。

1: public class UnityInstanceProvider : IInstanceProvider
2: {
3: private static object syncHelper = new object();
4: private static IDictionary<string, IList<UnityTypeMapping>> registeredTypeMappings;
5: public static IDictionary<string, IUnityContainer> Containers
6: { get; private set; }
7:
8: private IList<UnityTypeMapping> _registeredTypeMappings;
9: public Type ContractType
10: { get; private set; }
11: public IUnityContainer Container
12: { get; private set; }
13:
14: static UnityInstanceProvider()
15: {
16: registeredTypeMappings = new Dictionary<string, IList<UnityTypeMapping>>();
17: Containers = new Dictionary<string, IUnityContainer>();
18: }
19:
20: public UnityInstanceProvider(Type contractType, string containerName)
21: {
22: if (contractType == null)
23: {
24: throw new ArgumentNullException("contractType");
25: }
26:
27: this.ContractType = contractType;
28:
29: string key = containerName ?? string.Empty;
30: if (Containers.ContainsKey(key))
31: {
32: this.Container = Containers[key];
33: this._registeredTypeMappings = registeredTypeMappings[key];
34: return;
35: }
36:
37: UnityContainerElement containerElement = this.GetUnitySettings(containerName);
38: IUnityContainer container = new UnityContainer();
39: if (null != containerElement)
40: {
41: containerElement.Configure(container);
42: }
43: container.AddNewExtension<ExtendedInterception>();
44: PolicyInjectionSettings section = (PolicyInjectionSettings)ConfigurationSourceFactory.Create().GetSection("policyInjection");
45: if (section != null)
46: {
47: section.ConfigureContainer(this.Container, ConfigurationSourceFactory.Create());
48: }
49: lock (syncHelper)
50: {
51: if (!Containers.ContainsKey(key))
52: {
53: Containers[key] = container;
54: registeredTypeMappings[key] = containerElement.Types.Copy();
55: }
56: }
57:
58: this.Container = container;
59: this._registeredTypeMappings = registeredTypeMappings[key];
60: }
61:
62: #region IInstanceProvider Members
63:
64: public object GetInstance(InstanceContext instanceContext, Message message)
65: {
66: string contractServiceTypeMappingName = string.Empty;
67: var contractServiceTypeMappings = from mapping in this._registeredTypeMappings
68: where mapping.Type == this.ContractType &&
69: mapping.MapTo == instanceContext.Host.Description.ServiceType
70: select mapping;
71: if (contractServiceTypeMappings.Count() == 0)
72: {
73: contractServiceTypeMappingName = Guid.NewGuid().ToString();
74: this.Container.RegisterType(this.ContractType, instanceContext.Host.Description.ServiceType, contractServiceTypeMappingName);
75: this._registeredTypeMappings.Add(new UnityTypeMapping { Type = this.ContractType, MapTo = instanceContext.Host.Description.ServiceType, Name = contractServiceTypeMappingName });
76: }
77: else
78: {
79: contractServiceTypeMappingName = contractServiceTypeMappings.ToArray<UnityTypeMapping>()[0].Name;
80: }
81:
82: return this.Container.Resolve(this.ContractType, contractServiceTypeMappingName);
83: }
84:
85: private UnityContainerElement GetUnitySettings(string containerName)
86: {
87: UnityConfigurationSection unitySection = ConfigurationManager.GetSection("unity") as UnityConfigurationSection;
88: if (unitySection == null)
89: {
90: return null;
91: }
92:
93: if (string.IsNullOrEmpty(containerName))
94: {
95: return unitySection.Containers.Default;
96: }
97: else
98: {
99: return unitySection.Containers[containerName];
100: }
101: }
102:
103: public object GetInstance(InstanceContext instanceContext)
104: {
105: return this.GetInstance(instanceContext, null);
106: }
107:
108: public void ReleaseInstance(InstanceContext instanceContext, object instance)
109: {
110: IDisposable disposable = instance as IDisposable;
111: if (disposable != null)
112: {
113: disposable.Dispose();
114: }
115: }
116:
117: #endregion
118: }

三、為UnityInstanceProvider創建Behavor對象

自定義行為(Behavior)是進行WCF擴張最為典型和常用的方式。按照作用域的不同,WCF的行為可以分為以下四類:契約行為(Contract Behavior)、服務行為(Service Contract)、終結點行為(Endpoint Behavior)和操作行為(Operation Behavior)。為了將上面自定義的UnityInstanceProvider應用到WCF服務端的分發系統,定義了如下一個行為類型:UnityIntegrationBehaviorAttribute。我們可以看出,UnityIntegrationBehaviorAttribute同時實現了IServiceBehavior、IContractBehavior和IEndpointBehavior,所以既是一個服務行為,也是一個契約行為,同時還是一個終結點行為。同時UnityIntegrationBehaviorAttribute繼承了Attribbute,所以同時可以以Attribute的形式應用到服務契約(作為契約行為)類型和服務(作為服務類型)。你同樣可以通過配置的方式以服務行為和終結點行為的方式應用該UnityIntegrationBehaviorAttribute。如果想采用配置的方式,你還需要定義相關的BehaviorExtensionElement,由於篇幅的問題,我就不就對BehaviorExtensionElement的問題作介紹了。

1: using System;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
namespace Artech.UnityIntegration
{
public class UnityIntegrationBehaviorAttribute : Attribute, IServiceBehavior, IContractBehavior,IEndpointBehavior
{
public string ContainerName
{ get; set; }
#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
#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(endpoint.Contract.ContractType, this.ContainerName);
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
}
#endregion
#region IServiceBehavior Members
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
{
foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
{
endpointDispatcher.DispatchRuntime.InstanceProvider = new UnityInstanceProvider(GetContractType(serviceHostBase, endpointDispatcher), this.ContainerName);
}
}
}
private Type GetContractType(ServiceHostBase serviceHostBase, EndpointDispatcher endpointDispatcher)
{
var endpoint = serviceHostBase.Description.Endpoints.Where(item => item.Contract.Name == endpointDispatcher.ContractName &&
item.Contract.Namespace == endpointDispatcher.ContractNamespace).ToArray<ServiceEndpoint>()[0];
return endpoint.Contract.ContractType;
}
public void Validate(ServiceDescription serviceDescription,ServiceHostBase serviceHostBase)
{
}
#endregion
}
}

四、將UnityIntegrationBehavior應用到WCF應用中

為了演示UnityIntegrationBehavior的效果,我們創建了一個簡單的WCF實例應用。我們采用《Enterprise Library深入解析與靈活應用(7):再談PIAB與Unity之間的集成》中同步時間提供的例子,通過一個服務得到同步的當前時間。下面是服務契約的定義:

1: using System;
2: using System.ServiceModel;
3: namespace Artech.UnityIntegrationDemo.Contracts
4: {
5: [ServiceContract(Namespace="urn:artech.com")]
6: public interface ISyncTimeProvision
7: {
8: [OperationContract]
9: DateTime GetCurrentTime();
0: }
1: }

實現了該服務契約的SyncTimeProvisionService本是並不具體實現不同時間的提供,而是通過另一個組件SyncTimeProvider。你可以將SyncTimeProvider看成是同一個應用的另一個模塊,將此例子看成是一個典型的跨模塊調用。為了實現真正的模塊化,達到模塊之間的松耦合,我們借助Unity,采用“屬性注入(Propetry Setter Injection)”的方式,通過接口的方式(ISyncTimeProvider)調用另一個模塊。為了證實PIAB的效果,我在SyncTimeProvider上面應用了CachingCallHandlerAttribute,如果該CallHandler生效的話,方法返回的結果將會被緩存,在緩存過期之前,你將得到相同的時間。而我們定義的UnityIntegrationBehaviorAttribute以服務行為的方式直接應用到服務類型(SyncTimeProvisionService)上。

 1: using System;
2: using Artech.UnityIntegration;
3: using Artech.UnityIntegrationDemo.Contracts;
4: using Microsoft.Practices.Unity;
5:
6: namespace Artech.UnityIntegrationDemo.Services
7: {
8: [UnityIntegrationBehavior(ContainerName="myContainer")]
9: public class SyncTimeProvisionService : ISyncTimeProvision
10: {
12: [Dependency]
13: public ISyncTimeProvider SyncTimeProvider
14: { get; set; }
15:
16: #region ISyncTimeProvision Members
17:
18: public DateTime GetCurrentTime()
19: {
20: return this.SyncTimeProvider.GetCurrentTime();
21: }
22:
23: #endregion
24: }
25:
26: public interface ISyncTimeProvider
27: {
28: DateTime GetCurrentTime();
29: }
30:
31: [CachingCallHandler]
32: public class SyncTimeProvider : ISyncTimeProvider
33: {
34: #region ISyncTimeProvider Members
35:
36: public DateTime GetCurrentTime()
37: {
38: return DateTime.Now;
39: }
40:
41: #endregion
42: }
43: }

在服務寄宿的配置中,提供了WCF服務和Unity的相關設置:

 1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <configSections>
4: <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
5: </configSections>
6: <system.serviceModel>
7: <services>
8: <service name="Artech.UnityIntegrationDemo.Services.SyncTimeProvisionService">
9: <endpoint address="http://127.0.0.1:3721/synctimeprovisionservice"
10: binding="ws2007HttpBinding" bindingConfiguration="" contract="Artech.UnityIntegrationDemo.Contracts.ISyncTimeProvision" />
11: </service>
12: </services>
13: </system.serviceModel>
14: <unity>
15: <typeAliases>
16: <typeAlias alias="ISyncTimeProvider" type="Artech.UnityIntegrationDemo.Services.ISyncTimeProvider, Artech.UnityIntegrationDemo.Services, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
17: <typeAlias alias="SyncTimeProvider" type="Artech.UnityIntegrationDemo.Services.SyncTimeProvider, Artech.UnityIntegrationDemo.Services, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
18: </typeAliases>
19: <containers>
20: <container name="myContainer">
21: <types>
22: <type type="ISyncTimeProvider" mapTo="SyncTimeProvider" />
23: </types>
24: </container>
25: </containers>
26: </unity>
27: </configuration>

當服務成功寄宿,在Console Appliation下,執行下面一段服務端調用程序,你將得到下面的輸出。從輸出結果中,我們可以清晰地看到,返回的5個返回的時間均是相同的,由此我們可以看出應用才SyncTimeProvider上面的CachingCallHandlerAttribute生效了。進而證明了PIAB和Unity、Unity和WCF的有效集成:

 1: using System;
2: using System.ServiceModel;
3: using System.Threading;
4: using Artech.UnityIntegrationDemo.Contracts;
5:
6: namespace Artech.UnityIntegrationDemo.Client
7: {
8: class Program
9: {
10: static void Main(string[] args)
11: {
12: using (ChannelFactory<ISyncTimeProvision> channelFactory = new ChannelFactory<ISyncTimeProvision>("synctimeprovisionservice"))
13: {
14: ISyncTimeProvision proxy = channelFactory.CreateChannel();
15: using (proxy as IDisposable)
16: {
17: try
18: {
19: for (int i = 0; i < 5; i++)
20: {
21: Console.WriteLine("The current time is {0}", proxy.GetCurrentTime());
22: Thread.Sleep(1000);
23: }
24: }
25: catch (CommunicationException)
26: {
27: (proxy as ICommunicationObject).Abort();
28: throw;
29: }
30: catch (TimeoutException)
31: {
32: (proxy as ICommunicationObject).Abort();
33: throw;
34: }
35: }
36: }
37:
38: Console.Read();
39: }
40: }
41: }
42:

執行結果:

注:部分內容節選自《WCF技術剖析(卷1)》第十章: WCF實例研究(WCF in Practice)

本文配套源碼: http://www.bianceng.net/dotnet/201210/532.htm

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved