code4fun: one service,one config
WCF的承載既可以通過編碼(Code)實現,也能夠通過配置(Config)實現.而且使用配置,更有利于日后的維護和擴展.WCF缺省的是將全部的服務配置都寫到一個config文件中去.這種方式更有利于專業的IT人員對服務進行集中式管理.在很多項目中,這種方式也能解決分工問題.但是它也有一定的弊端,比如: 一個無關緊要的service變動,就需要變動config,而對應用程序config的任何修改非常有可能導致應用程序的重啟或者異常.比如在網站中,如果更改web.config會導致Session,Application等的丟失.這些情況就會影響用戶的體驗.本文使用自定義ServiceHost的方式,實現一個Service對應一個config文件,這樣當有service發生修改的時候,管理人員就只需要更改對應的config文件。更好的實現了服務與服務之間的隔離。
1)自定義配置文件的實現思路和關鍵點
在ServiceHost的父類ServiceHostBase中,有一個和配置文件的加載密切相關的方法,它為:
protected virtual void ApplyConfiguration();
這個方法用于將應用程序配置文件中<system.serviceModel>節點下的配置信息,轉換成WCF的具體服務設置。那么重寫這個方法,應該就能實現上述目的。
2)自定義配置文件的策略
缺省的策略是服務與配置是多對一的關系,一個配置中包含全部的服務配置。那配置文件也就是默認的應用程序的配置文件了,在網站中是web.config,而在windows應用程序中,配置文件是應用程序名.config。而本文中要實現的自定義配置的策略是一個服務就對應一個配置文件,配置文件的名稱就是服務的全名。比如有一個服務名字為:Robin.Wcf.OnetoOne.Services.Calculator,那與其對應的服務配置文件名稱就是:Robin.Wcf.OnetoOne.Services.Calculator.config。同時還需要考慮這個文件的位置。我們制定一個策略:自定義的服務配置文件和缺省應用程序配置文件存放到同一個目錄。
實現過程:
第一步:創建一個解決方案,名稱為:Robin.Wcf.OnetoOne,并在其中添加三個項目,分別為:
Robin.Wcf.OnetoOne.ServiceLib
Robin.Wcf.OnetoOne.Services
Robin.Wcf.OnetoOne.Services.Web
這三個項目的作用分別為:
Robin.Wcf.OnetoOne.ServiceLib中用于創建契約和實現服務,并且創建自定義的ServiceHost和自定義的ServiceHostFactory
Robin.Wcf.OnetoOne.Services是對Robin.Wcf.OnetoOne.ServiceLib實現服務的一種自托管(self hosting),它最終是一個Console Application
Robin.Wcf.OnetoOne.Services.Web是對Robin.Wcf.OnetoOne.ServiceLib實現服務的iis托管。
第二步:實現服務
在Robin.Wcf.OnetoOne.ServiceLib中創建兩個文件:ICalculator.cs和Calculator.cs,它們分別用于服務契約和服務實現,具體代碼如下:
ICalculator.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; namespace Robin.Wcf.OnetoOne.Services { [ServiceContract] public interface ICalculator { [OperationContract] int Add(int a, int b); } }
Calculator.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Robin.Wcf.OnetoOne.Services { public class Calculator:ICalculator { public int Add(int a, int b) { return a + b; } } }
實現的服務非常的簡單,因為我們的目的也不是要實現非常復雜的業務邏輯,而關鍵在于修改框架的使用方式上面。
第三步:實現自定義的ServiceHost,名稱為:MyServiceHost。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.ServiceModel.Configuration; using System.Configuration; namespace Robin.Wcf.OnetoOne.Services { public class MyServiceHost:ServiceHost { public MyServiceHost(object singletonInstance, params Uri[] baseAddresses) : base(singletonInstance, baseAddresses) { } public MyServiceHost(Type serviceType, params Uri[] baseAddresses):base(serviceType,baseAddresses) { } /// <summary> /// override ApplyConfiguration to load config from custom file /// </summary> protected override void ApplyConfiguration() { //get custom config file name by our rule: config file name = ServiceType.Name var myConfigFileName = this.Description.ServiceType.FullName; //get config file path string dir = System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase; string myConfigFilePath = System.IO.Path.Combine(dir, myConfigFileName + ".config"); if (!System.IO.File.Exists(myConfigFilePath)) { base.ApplyConfiguration(); return; } var configFileMap = new System.Configuration.ExeConfigurationFileMap(); configFileMap.ExeConfigFilename = myConfigFilePath; var config = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigurationUserLevel.None); var serviceModel = System.ServiceModel.Configuration.ServiceModelSectionGroup.GetSectionGroup(config); if (serviceModel == null) { base.ApplyConfiguration(); return; } foreach (ServiceElement serviceElement in serviceModel.Services.Services) { if (serviceElement.Name == this.Description.ServiceType.FullName) { LoadConfigurationSection(serviceElement); return; } } throw new Exception("there is no service element match the description!"); } } }
在自定義的ServiceHost中,主要是通過重寫(override)ApplyConfiguration方法來實現,將服務的全名,也就是this.Description.ServiceType.FullName作為配置文件的文件名,然后仍然使用.config作為后綴。使用缺省應用程序配置文件的路徑作為自定義配置文件路徑,缺省的配置文件路徑的獲取方法是:
string dir = System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
下面涉及到一個難題,如何將自定義文件作為系統配置文件,下面的代碼段解決了這個問題:
var configFileMap = new System.Configuration.ExeConfigurationFileMap(); configFileMap.ExeConfigFilename = myConfigFilePath; var config = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigurationUserLevel.None);
最后得到的config是System.Configuration.Configuration類別的一個實例。
將自定義的文件作為配置并生成配置對象后,接下來要做的就是在此Configuration對象中尋找ServiceModeSectionGroup,然后通過在ServiceModeSectionGroup中獲取
和當前要承載的服務名稱相等的ServiceElement,方法如下面的代碼所示:
var serviceModel = System.ServiceModel.Configuration.ServiceModelSectionGroup.GetSectionGroup(config); if (serviceModel == null) { base.ApplyConfiguration(); return; } foreach (ServiceElement serviceElement in serviceModel.Services.Services) { if (serviceElement.Name == this.Description.ServiceType.FullName) { LoadConfigurationSection(serviceElement); return; } } throw new Exception("there is no service element match the description!");
第四步:實現自定義的ServcieHostFactory:MyServiceHostFactory
如果承載單純是Consol Application或者其他非Web應用程序,實現了第三步就可以了。但在Web的.svc中,沒有顯示的指定ServiceHost的Factory,那它在默認情況下是使用ServiceHostFactory的,而ServiceHostFactory產生的是ServiceHost對象,想要使用我們上一步中自定義的MyServiceHost,需要實現一個自定義的ServcieHostFactory,實現代碼非常簡單:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel.Activation; using System.ServiceModel; using System.Reflection; namespace Robin.Wcf.OnetoOne.Services { public class MyServiceHostFactory:ServiceHostFactory { protected override System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses) { return new MyServiceHost(serviceType, baseAddresses); } } }
其實,就是在CreateServiceHost方法中返回一個MyServiceHost就可以了。
第五步:實現Console的承載
在Robin.Wcf.OnetoOne.Services中,新建一個config文件,名稱為:Robin.Wcf.OnetoOne.Services.Calculator.config,注意名稱和已經實現的服務的全名稱一致。將其內容設置為:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <services> <service name="Robin.Wcf.OnetoOne.Services.Calculator"> <host> <baseAddresses> <add baseAddress="http://127.0.0.1:5698"/> </baseAddresses> </host> <endpoint address="Calculator" binding="wsHttpBinding" contract="Robin.Wcf.OnetoOne.Services.ICalculator"></endpoint> </service> </services> </system.serviceModel> </configuration>
注意了,自定義的配置文件也是配置文件,它也需要和缺省應用程序的配置文件格式一致,首先要有<configuration> 節點。
然后,設置Programe.cs的代碼為:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Robin.Wcf.OnetoOne.Services { public class Programe { static void Main(string[] args) { using(MyServiceHost host = new MyServiceHost(typeof(Calculator))) { host.Opened+=new EventHandler(delegate(object sender,EventArgs e){ Console.WriteLine("Service is Opened!"); foreach (var channelDiapacher in host.ChannelDispatchers) { Console.WriteLine("listen url:" + channelDiapacher.Listener.Uri.ToString()); } }); host.Open(); } Console.Read(); } } }
第六步:實現IIS承載
在Robin.Wcf.OnetoOne.Services.Web中,首先引用Robin.Wcf.OnetoOne.ServiceLib,然后同樣添加一個自定義配置文件:Robin.Wcf.OnetoOne.Services.Calculator.config。代碼為:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <services> <service name="Robin.Wcf.OnetoOne.Services.Calculator" behaviorConfiguration="Robin.Wcf.OnetoOne.WebHost.Service1Behavior"> <endpoint binding="wsHttpBinding" contract="Robin.Wcf.OnetoOne.Services.ICalculator"></endpoint> </service> </services> <behaviors> <serviceBehaviors> <behavior name="Robin.Wcf.OnetoOne.WebHost.Service1Behavior"> <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="true" /> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> </configuration>
將Service.svc的代碼更改為:
<%@ ServiceHost Language="C#" Debug="true" Service="Robin.Wcf.OnetoOne.Services.Calculator" Factory="Robin.Wcf.OnetoOne.Services.MyServiceHostFactory" %>
注意Service與Factory的配置。
接下來的一步就是驗證我們的實現是否可行了,啟動Robin.Wcf.OnetoOne.Services,得到如下的程序輸出:
結合上面的代碼我們可以發現,配置在Robin.Wcf.OnetoOne.Services.Calculator.config中的配置已經起到了預期的效果。說明在Console中可行
下面啟動Robin.Wcf.OnetoOne.Services.Web中的Service.svc,能得到如下的輸出結果:
表明,在iis的承載中也工作正常。
參考資料:
Custom Config file for a WCF Service hosted in IIS
示例代碼:https://files.cnblogs.com/jillzhang/Robin.Wcf.OnetoOne.rar
Next Topic : One Host,Many Service!
出處:http://jillzhang.cnblogs.com/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。

浙公網安備 33010602011771號