tags:

views:

2411

answers:

4

In an application that is hosting several WCF services, what would be the best way to add custom configuration information for each service? For example you may want to pass or set a company name or specify the connectionString a service or some other parameter.

I'm guessing this might be possible by implementing IServiceBehavior.

i.e 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 set ABC on MyService and DEF on MyOtherService (assuming they have some common interface with a company name).

Can anyone elaborate on how you implement this?

TIA

Michael

A: 

It depends a lot of where and how you expect to use said information. If it's not something that's going to do a lot with the infrastructure (i.e. getting the services to run and processing requests), I'd be tempted to say that trying to push that into the WCF behaviors might be adding more complexity than it's worth. It would probably be simpler to just use a custom configuration section of your own.

Could you clarify how you expect to use this information at runtime? Maybe that way we can provide more explicit advice...

tomasr
A: 

Well, I thought the example I've given was pretty elaborate. I'll try to elaborate further...

Basically I want to be able to pass custom configuration data to a WCF service in an application where there may be several WCF services running.

So what that means is in the an instance of a service running in the application I want to access data that has been configured specifically for that service (and not another service). I guess I could do this simply by using the application settings and using the service's type as the key. I was hoping WCF might have some better constructs for this.

+1  A: 

I know this is old, but it was never marked answered, so I thought I'd take a shot. If I understand what you're after, you can do it with a custom ServiceHostFactory.
Good post on this here.

You set up yuour custom ServiceHostFactory like so:

<%@ 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. Normally for WCF apps hosted in IIS, WCF would automatically look for config in web.config. In this example, we override that behavior to first look for a config 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 could replace this with "anything" - for example, looking for config 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");
}
Cheeso
A: 

I was having a similar issue, but I was using DuplexChannel. Based on a post i found I found I solved 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

pablito