WCF的承載既可以通過編碼實現,也能夠通過配置實現.而且使用配置,更有利於日後的維護和擴展。我們經常會碰到這樣的一個場景:需要把WCF的配置信息放在一個單獨的文件中,這種情況經常出現在需要為自己開發的服務配置,需要采用獨立的配置文件,而不是只能放到app.config/web.config中。.NET提供了一種機制是通過ConfigSource。例如在asp.net的在站點的默認 Web.Config 文件中使用:
<appSettings configSource="customAppSetting.config"/>
然後新建 customAppSetting.Config 文件:
<?xml version="1.0" encoding="utf-8"?> <appSettings> <add key="IsDev" value="True"/> </appSettings>
在網站運行時,如果修改 Web.Config 文件會引起站點的重啟,而修改 My.Config 文件則不會,同時也提高了配置文件的可讀性。
然而WCF的配置上configSource是無效的,那麼WCF如何自定義配置文件?
WCF的ServiceHost和ChannelFactory<T>分別提供了服務端和客戶端的可擴展解決方案。下面針對這兩個對象分別說明如何自定義服務端和客戶端的配置文件。
1、服務端自定義配置文件:在ServiceHost的父類ServiceHostBase中,有一個和配置文件的加載密切相關的方法,它為:
protected virtual void ApplyConfiguration();
這個方法用於將應用程序配置文件中<system.serviceModel>節點下的配置信息,轉換成WCF的具體服務設置。那麼重寫這個方法,代碼如下:
/// <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!"); } } }
2、WCF的客戶端自定義配置文件,WCF可以通過兩種方式構建代理,ClientBase<T>和ChannelFactory<T>,ClientBase最終也是通過ChannelFactory<T>來構建Channel的
ChannelFactory<T>有兩個方法為自定義配置文件提供解決方案:
protected virtual void ApplyConfiguration(string configurationName);
protected abstract ServiceEndpoint CreateDescription();
ApplyConfiguration方法和ServiceHost的ApplyConfiguration方法的功能類似,但是有一點不同的是需要和CreateDescription交互。其實ApplyConfiguration並不是客戶端代理這裡所要關注的地方,我們只需要關注CreateDescription就可以了。
ChannelFactory<T>的方法CreateDescription實現上是從默認配置文件(缺省AppDomain的配置文件),所以我們通過重寫這個方法就可以實現從外部文件加載配置。
/// <summary> /// Loads the serviceEndpoint description from the specified configuration file /// </summary> /// <returns></returns> protected override ServiceEndpoint CreateDescription() { ServiceEndpoint serviceEndpoint = base.CreateDescription(); ExeConfigurationFileMap map = new ExeConfigurationFileMap(); map.ExeConfigFilename = this.configurationPath; Configuration config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None); ServiceModelSectionGroup group = ServiceModelSectionGroup.GetSectionGroup(config); ChannelEndpointElement selectedEndpoint = null; foreach (ChannelEndpointElement endpoint in group.Client.Endpoints) { if (endpoint.Contract == serviceEndpoint.Contract.ConfigurationName) { selectedEndpoint = endpoint; break; } } if (selectedEndpoint != null) { if (serviceEndpoint.Binding == null) { serviceEndpoint.Binding = CreateBinding(selectedEndpoint.Binding, group); } if (serviceEndpoint.Address == null) { serviceEndpoint.Address = new EndpointAddress(selectedEndpoint.Address, GetIdentity(selectedEndpoint.Identity), selectedEndpoint.Headers.Headers); } if (serviceEndpoint.Behaviors.Count == 0 && selectedEndpoint.BehaviorConfiguration != null) { AddBehaviors(selectedEndpoint.BehaviorConfiguration, serviceEndpoint, group); } serviceEndpoint.Name = selectedEndpoint.Contract; } return serviceEndpoint; }
具體的實現可以參看例子代碼,這個例子WCF sdk的例子ICalculator。