WCF Service Configuration

In an application that hosts multiple WCF services, what's the best way to add user configuration information for each service? For example, you can pass or set the name of the company or specify connectionString for the service or some other parameter.

I assume this is possible with the implementation of IServiceBehavior.

Something like....

<behaviors> <serviceBehaviors> <behavior name="MyBehavior"> <serviceMetadata httpGetEnabled="true" /> <serviceDebug /> <customBehavior myCompany="ABC" /> </behavior> <behavior name="MyOtherBehavior"> <serviceMetadata httpGetEnabled="true" /> <serviceDebug /> <customBehavior myCompany="DEF" /> </behavior> </serviceBehaviors> </behaviors> <services> <service behaviorConfiguration="MyBehavior" name="MyNameSpace.MyService"> <endpoint address="" behaviorConfiguration="" binding="netTcpBinding" name="TcpEndpoint" contract="MyNameSpace.IMyService" /> <endpoint address="mex" binding="mexTcpBinding" bindingConfiguration="" name="TcpMexEndpoint" contract="IMetadataExchange" /> <host> <baseAddresses> <add baseAddress="net.tcp://localhost:4000/MyService" /> </baseAddresses> </host> </service> <service behaviorConfiguration="MyOtherBehavior" name="MyNameSpace.MyOtherService"> <endpoint address="" behaviorConfiguration="" binding="netTcpBinding" name="TcpEndpoint" contract="MyNameSpace.IMyOtherService" /> <endpoint address="mex" binding="mexTcpBinding" bindingConfiguration="" name="TcpMexEndpoint" contract="IMetadataExchange" /> <host> <baseAddresses> <add baseAddress="net.tcp://localhost:4000/MyOtherService" /> </baseAddresses> </host> </service> </services> 

Would install ABC on MyService and DEF on MyOtherService (provided that they have a common interface with the company name).

Can someone clarify how you implement this?

TIA

Michael

+6
source share
4 answers

I know this is old, but there has never been an answer to it, so I thought I would take a picture. If I understand what you need, you can do this using the special ServiceHostFactory service.
Good post on this here .

You configured your custom ServiceHostFactory like this:

 <%@ ServiceHost Language="C#" Debug="true" Service="Ionic.Samples.Webservices.Sep20.CustomConfigService" Factory="Ionic.ServiceModel.ServiceHostFactory"%> 

Then in your ServiceHostFactory you can override a method called ApplyConfiguration. Typically, for WCF applications hosted in IIS, WCF automatically looks for configuration in web.config. In this example, we override this behavior to first look for a configuration file named after the WCF service description.

 protected override void ApplyConfiguration() { // generate the name of the custom configFile, from the service name: string configFilename = System.IO.Path.Combine ( physicalPath, String.Format("{0}.config", this.Description.Name)); if (string.IsNullOrEmpty(configFilename) || !System.IO.File.Exists(configFilename)) base.ApplyConfiguration(); else LoadConfigFromCustomLocation(configFilename); } 

You can replace this with "anything" - for example, searching for a configuration in a database table.

A few more methods complete the puzzle.

 private string _physicalPath = null; private string physicalPath { get { if (_physicalPath == null) { // if hosted in IIS _physicalPath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath; if (String.IsNullOrEmpty(_physicalPath)) { // for hosting outside of IIS _physicalPath= System.IO.Directory.GetCurrentDirectory(); } } return _physicalPath; } } private void LoadConfigFromCustomLocation(string configFilename) { var filemap = new System.Configuration.ExeConfigurationFileMap(); filemap.ExeConfigFilename = configFilename; System.Configuration.Configuration config = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration (filemap, System.Configuration.ConfigurationUserLevel.None); var serviceModel = System.ServiceModel.Configuration.ServiceModelSectionGroup.GetSectionGroup(config); bool loaded= false; foreach (System.ServiceModel.Configuration.ServiceElement se in serviceModel.Services.Services) { if(!loaded) if (se.Name == this.Description.ConfigurationName) { base.LoadConfigurationSection(se); loaded= true; } } if (!loaded) throw new ArgumentException("ServiceElement doesn't exist"); } 
+8
source share

I had a similar problem, but I used DuplexChannel. Based on the post , I found that I found that I decided this way:

 public class CustomDuplexChannelFactory<TChannel> : DuplexChannelFactory<TChannel> { public static string ConfigurationPath { get; set; } public CustomDuplexChannelFactory(InstanceContext callbackInstance) : base(callbackInstance) { } protected override ServiceEndpoint CreateDescription() { ServiceEndpoint serviceEndpoint = base.CreateDescription(); if(ConfigurationPath == null || !File.Exists(ConfigurationPath)) return base.CreateDescription(); ExeConfigurationFileMap executionFileMap = new ExeConfigurationFileMap(); executionFileMap.ExeConfigFilename = ConfigurationPath; System.Configuration.Configuration config = ConfigurationManager.OpenMappedExeConfiguration(executionFileMap, ConfigurationUserLevel.None); ServiceModelSectionGroup serviceModeGroup = ServiceModelSectionGroup.GetSectionGroup(config); ChannelEndpointElement selectedEndpoint = null; foreach(ChannelEndpointElement endpoint in serviceModeGroup.Client.Endpoints) { if(endpoint.Contract == serviceEndpoint.Contract.ConfigurationName) { selectedEndpoint = endpoint; break; } } if(selectedEndpoint != null) { if(serviceEndpoint.Binding == null) { serviceEndpoint.Binding = CreateBinding(selectedEndpoint.Binding, serviceModeGroup); } if(serviceEndpoint.Address == null) { serviceEndpoint.Address = new EndpointAddress(selectedEndpoint.Address, GetIdentity(selectedEndpoint.Identity), selectedEndpoint.Headers.Headers); } if(serviceEndpoint.Behaviors.Count == 0 && !String.IsNullOrEmpty(selectedEndpoint.BehaviorConfiguration)) { AddBehaviors(selectedEndpoint.BehaviorConfiguration, serviceEndpoint, serviceModeGroup); } serviceEndpoint.Name = selectedEndpoint.Contract; } return serviceEndpoint; } private Binding CreateBinding(string bindingName, ServiceModelSectionGroup group) { BindingCollectionElement bindingElementCollection = group.Bindings[bindingName]; if(bindingElementCollection.ConfiguredBindings.Count > 0) { IBindingConfigurationElement be = bindingElementCollection.ConfiguredBindings[0]; Binding binding = GetBinding(be); if(be != null) { be.ApplyConfiguration(binding); } return binding; } return null; } private void AddBehaviors(string behaviorConfiguration, ServiceEndpoint serviceEndpoint, ServiceModelSectionGroup group) { EndpointBehaviorElement behaviorElement = group.Behaviors.EndpointBehaviors[behaviorConfiguration]; for(int i = 0; i < behaviorElement.Count; i++) { BehaviorExtensionElement behaviorExtension = behaviorElement[i]; object extension = behaviorExtension.GetType().InvokeMember("CreateBehavior", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, behaviorExtension, null); if(extension != null) { serviceEndpoint.Behaviors.Add((IEndpointBehavior)extension); } } } private EndpointIdentity GetIdentity(IdentityElement element) { EndpointIdentity identity = null; PropertyInformationCollection properties = element.ElementInformation.Properties; if(properties["userPrincipalName"].ValueOrigin != PropertyValueOrigin.Default) { return EndpointIdentity.CreateUpnIdentity(element.UserPrincipalName.Value); } if(properties["servicePrincipalName"].ValueOrigin != PropertyValueOrigin.Default) { return EndpointIdentity.CreateSpnIdentity(element.ServicePrincipalName.Value); } if(properties["dns"].ValueOrigin != PropertyValueOrigin.Default) { return EndpointIdentity.CreateDnsIdentity(element.Dns.Value); } if(properties["rsa"].ValueOrigin != PropertyValueOrigin.Default) { return EndpointIdentity.CreateRsaIdentity(element.Rsa.Value); } if(properties["certificate"].ValueOrigin != PropertyValueOrigin.Default) { X509Certificate2Collection supportingCertificates = new X509Certificate2Collection(); supportingCertificates.Import(Convert.FromBase64String(element.Certificate.EncodedValue)); if(supportingCertificates.Count == 0) { throw new InvalidOperationException("UnableToLoadCertificateIdentity"); } X509Certificate2 primaryCertificate = supportingCertificates[0]; supportingCertificates.RemoveAt(0); return EndpointIdentity.CreateX509CertificateIdentity(primaryCertificate, supportingCertificates); } return identity; } private Binding GetBinding(IBindingConfigurationElement configurationElement) { if(configurationElement is CustomBindingElement) return new CustomBinding(); else if(configurationElement is BasicHttpBindingElement) return new BasicHttpBinding(); else if(configurationElement is NetMsmqBindingElement) return new NetMsmqBinding(); else if(configurationElement is NetNamedPipeBindingElement) return new NetNamedPipeBinding(); else if(configurationElement is NetPeerTcpBindingElement) return new NetPeerTcpBinding(); else if(configurationElement is NetTcpBindingElement) return new NetTcpBinding(); else if(configurationElement is WSDualHttpBindingElement) return new WSDualHttpBinding(); else if(configurationElement is WSHttpBindingElement) return new WSHttpBinding(); else if(configurationElement is WSFederationHttpBindingElement) return new WSFederationHttpBinding(); return null; } } 

I summarized this in my blog

+3
source share

It depends on where and how you are going to use the specified information. If this is not something that can do much with the infrastructure (i.e., getting services to run and process requests), I will be tempted to say that trying to insert this into the WCF behavior may be more difficult than it costs. It would probably be easier to just use your own custom configuration section.

Could you explain how you intend to use this information at runtime? Perhaps in this way we can provide clearer advice ...

0
source share

Well, I thought the example I gave was pretty complicated. I will try to stop in more detail ...

Basically, I want to be able to pass custom configuration data to a WCF service in an application where several WCF services can run.

So, what does this mean, in the instance of the service running in the application, I want to access the data that was configured specifically for this service (and not another service). I think I could do this simply by using the application settings and using the service type as the key. I was hoping that WCF could have some improved constructs for this.

0
source share

All Articles