WCF服務的批量寄宿
如果采用自我寄宿的方式,我們需要為每個寄宿的服務創建ServiceHost對象。但是一個應用往往具有很多服務需要被發布,基於單個服務的ServiceHost的創建將會變成一個很繁瑣的事情。如果我們能夠采用某種機製來讀取所有配置的服務,並自動為它們創建相應的ServiceHost對象,這無疑是一種理想的方式。[源代碼從這裏下載]
我想很多人想到了直接讀取表示寄宿服務的<system.serviceModel>/<services>/<service>配置元素列表,通過其name配置屬性得到表示服務的“類型”,並據此創建相應的ServiceHost對象。這種做法是不被推薦的,原因有二:
- <service>配置元素的name屬性並不是寄宿服務的類型全名,而是通過ServiceBehaviorAttribute特性對應的服務配置名稱;
- 即使我們不對服務的配置名稱作顯式設置,讓該名稱表示成服務類型全名,但是由於它並包含程序集名稱,我們往往不得不加載所有可用的程序集。
我們可以將需要需要批量寄宿的服務類型定義在配置文件中。很多人喜歡直接采用<appSettings>作為自定義的配置,但是我個人是既不推薦這種做法的,我覺得自定義結構化的配置節是更好的選擇。批量寄宿的服務類型就定義在具有如下結構的 <artech.batchingHosting>配置節下。
1: <artech.batchingHosting>
2: <add type="Artech.BatchingHosting.FooService, Artech.BatchingHosting"/>
3: <add type="Artech.BatchingHosting.BarService, Artech.BatchingHosting"/>
4: <add type="Artech.BatchingHosting.BazService, Artech.BatchingHosting"/>
5: </artech.batchingHosting>
上麵XML表示的自定義配置節通過具有如下定義的BatchingHostingSettings表示。BatchingHostingSettings包含一個通過ServiceTypeElementCollection表示的配置元素集合,而具體的配置元素類型為ServiceTypeElement。而ServiceTypeElement的配置ServiceType表示具體的服務類型。
1: public class BatchingHostingSettings: ConfigurationSection
2: {
3: [ConfigurationProperty("", IsDefaultCollection = true)]
4: public ServiceTypeElementCollection ServiceTypes
5: {
6: get { return (ServiceTypeElementCollection)this[""]; }
7: }
8:
9: public static BatchingHostingSettings GetSection()
10: {
11: return ConfigurationManager.GetSection("artech.batchingHosting")
12: as BatchingHostingSettings;
13: }
14: }
15: public class ServiceTypeElementCollection : ConfigurationElementCollection
16: {
17: protected override ConfigurationElement CreateNewElement()
18: {
19: return new ServiceTypeElement();
20: }
21: protected override object GetElementKey(ConfigurationElement element)
22: {
23: ServiceTypeElement serviceTypeElement = (ServiceTypeElement)element;
24: return serviceTypeElement.ServiceType.MetadataToken;
25: }
26: }
27: public class ServiceTypeElement : ConfigurationElement
28: {
29: [ConfigurationProperty("type",IsRequired = true)]
30: [TypeConverter(typeof(AssemblyQualifiedTypeNameConverter))]
31: public Type ServiceType
32: {
33: get { return (Type)this["type"]; }
34: set { this["type"] = value; }
35: }
36: }
ServiceTypeElement的ServiceType屬性上應用了一個TypeConverterAttribute特性並將類型轉換器類型設置為AssemblyQualifiedTypeNameConverter,這是為了讓配置係統能夠自動實現以字符串表示的配置屬性值與Type對象之間的轉換。這個類型轉換器是我們自定義的,具體定義如下:
1: public class AssemblyQualifiedTypeNameConverter : ConfigurationConverterBase
2: {
3: public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
4: {
5: string typeName = (string)value;
6: if (string.IsNullOrEmpty(typeName))
7: {
8: return null;
9: }
10: Type result = Type.GetType(typeName, false);
11: if (result == null)
12: {
13: throw new ArgumentException(string.Format("不能加載類型\"{0}\"", typeName));
14: }
15: return result;
16: }
17: public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
18: {
19: Type type = value as Type;
20: if (null == type)
21: {
22: throw new ArgumentNullException("value");
23: }
24: return type.AssemblyQualifiedName;
25: }
26: }
真正的服務批量寄宿是通過具有如下定義的ServiceHostCollection來實現的。ServiceHostCollection本質上就是一個ServiceHost的集合,我們可以通過構造函數和自定義的Add方法為指定的一組服務類型創建ServiceHost。在構造函數中,我們通過加載BatchingHostingSettings配置節的方式獲取需要批量寄宿的服務類型,並為之創建ServiceHost。
1: public class ServiceHostCollection : Collection<ServiceHost>, IDisposable
2: {
3: public ServiceHostCollection(params Type[] serviceTypes)
4: {
5: BatchingHostingSettings settings = BatchingHostingSettings.GetSection();
6: foreach (ServiceTypeElement element in settings.ServiceTypes)
7: {
8: this.Add(element.ServiceType);
9: }
10:
11: if (null != serviceTypes)
12: {
13: Array.ForEach<Type>(serviceTypes, serviceType=> this.Add(new ServiceHost(serviceType)));
14: }
15: }
16: public void Add(params Type[] serviceTypes)
17: {
18: if (null != serviceTypes)
19: {
20: Array.ForEach<Type>(serviceTypes, serviceType => this.Add(new ServiceHost(serviceType)));
21: }
22: }
23: public void Open()
24: {
25: foreach (ServiceHost host in this)
26: {
27: host.Open();
28: }
29: }
30: public void Dispose()
31: {
32: foreach (IDisposable host in this)
33: {
34: host.Dispose();
35: }
36: }
37: }
定義在ServiceHostCollection中的Open方法實現了對所有ServiceHost對象的批量開啟。ServiceHostCollection還實現了IDisposable接口,並在Dispose方法中實現了對ServiceHost的批量關閉。
現在我們定義了FooService、BarService和BazService三個服務類型,它們分別實現了契約接口IFoo、IBar和IBar。三個服務以及包含的終結點定義在如下的配置中,而三個服務類型同時被定義在了我們自定義的<artech.batchingHosting>配置節下。
1: <configuration>
2: <configSections>
3: <section name="artech.batchingHosting"
4: type="Artech.BatchingHosting.Configuration.BatchingHostingSettings,
5: Artech.BatchingHosting"/>
6: </configSections>
7: <system.serviceModel>
8: <services>
9: <service name="Artech.BatchingHosting.FooService">
10: <endpoint address="https://127.0.0.1:3721/fooservice"
11: binding="ws2007HttpBinding"
12: contract="Artech.BatchingHosting.IFoo"/>
13: </service>
14: <service name="Artech.BatchingHosting.BarService">
15: <endpoint address="https://127.0.0.1:3721/barservice"
16: binding="ws2007HttpBinding"
17: contract="Artech.BatchingHosting.IBar"/>
18: </service>
19: <service name="Artech.BatchingHosting.BazService">
20: <endpoint address="https://127.0.0.1:3721/bazservice"
21: binding="ws2007HttpBinding"
22: contract="Artech.BatchingHosting.IBaz"/>
23: </service>
24: </services>
25: </system.serviceModel>
26: <artech.batchingHosting>
27: <add type="Artech.BatchingHosting.FooService, Artech.BatchingHosting"/>
28: <add type="Artech.BatchingHosting.BarService, Artech.BatchingHosting"/>
29: <add type="Artech.BatchingHosting.BazService, Artech.BatchingHosting"/>
30: </artech.batchingHosting>
31: </configuration>
要實現針對三個服務的批量寄宿,我們隻需要創建ServiceHostCollection對象並開啟它即可。為了確認三個服務對應的ServiceHost確實被創建並被開啟,我通過如下的代碼注冊了ServiceHostCollection中每個ServiceHost的Opened事件。當該事件觸發時,會在控製台上打印一段文字。
1: using (ServiceHostCollection hosts = new ServiceHostCollection())
2: {
3: foreach (ServiceHost host in hosts)
4: {
5: host.Opened += (sender, arg) => Console.WriteLine("服務{0}開始監聽",
6: (sender as ServiceHost).Description.ServiceType);
7: }
8: hosts.Open();
9: Console.Read();
10: }
上麵這段代碼執行之後,控製台上將會具有如下一段輸出文字,這充分證明了我們對三個服務成功地進行了批量寄宿。
1: 服務Artech.BatchingHosting.FooService開始監聽
2: 服務Artech.BatchingHosting.BarService開始監聽
3: 服務Artech.BatchingHosting.BazService開始監聽
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
最後更新:2017-10-26 14:34:19