none
Config Hosting multiple WCF services in one NT service

    Question

  • Hello there,

     

    I've a question about hosting a service in a selfhosting scenario (not IIS or WAS).

     

    For example, I have one WCFService with a config file (web.config). In this config file a system.serviceModel section exists, but also several appSettings.

     

    Hosting in IIS is very simple. I created a svc file and voila, you can call the service. I do not have to place the configsettings of the WCFService into IIS.

     

    Now I turn to host the WCFService in another host, a Windows NT service (or a win/console app).

     

    Why do I have to place all of the WCFService configuration settings in the app.config of the NT Service? Why not just a reference to the physical path of the WCFService.

     

    In the case of three WCFServices hosted by one NT Service, I have to place three web.config contents into the app.config of the NT Service. (Am I wrong? I hope so.)

     

    tnx for reply!

     

    Marijn

     

    Monday, July 07, 2008 12:47 PM

Answers

  • You can load and deserialise client config from a single global location using the custom channel factory:

    Code Snippet

    using System;
    using System.Configuration;
    using System.Reflection;
    using System.Security.Cryptography.X509Certificates;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Configuration;
    using System.ServiceModel.Description;

    namespace WcfService
    {
    /// <summary>
    /// A custom channel factory which takes in a path to a
    /// custom configuration file
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class CustomChannelFactory<T> : ChannelFactory<T>
    {
    string _configurationPath;

    /// <summary>
    /// custom client channel constructor which
    /// specifies an external configuration file
    /// </summary>
    /// <param name="configurationPath"></param>
    public CustomChannelFactory(string configurationPath)
    : base(typeof(T))
    {
    _configurationPath = configurationPath;
    base.InitializeEndpoint((string)null, null);
    }

    /// <summary>
    /// overrides the CreateDescription() method of the channel factory
    /// to apply a new configuration file
    /// </summary>
    /// <returns></returns>
    protected override ServiceEndpoint CreateDescription()
    {
    ServiceEndpoint serviceEndpoint = 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;
    }

    /// <summary>
    /// Configures the binding for the selected endpoint
    /// </summary>
    /// <param name="bindingName"></param>
    /// <param name="group"></param>
    /// <returns></returns>
    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;
    }

    /// <summary>
    /// Adds the configured behavior to the selected endpoint
    /// </summary>
    /// <param name="behaviorConfiguration"></param>
    /// <param name="serviceEndpoint"></param>
    /// <param name="group"></param>
    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);
    }
    }
    }

    /// <summary>
    /// Gets the endpoint identity from the configuration file
    /// </summary>
    /// <param name="element"></param>
    /// <returns></returns>
    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;
    }

    /// <summary>
    /// Helper method to create the right binding depending on the configuration element
    /// </summary>
    /// <param name="configurationElement"></param>
    /// <returns></returns>
    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;
    }
    }
    }



    ...and call it like this:

    CustomChannelFactory<MyService> customChannelFactory = new CustomChannelFactory<MyService>(configFilePath);

    ...this would obviously be in the client code calling the service - but perhaps you could modify it for the actual hosted service?
    Monday, July 07, 2008 3:07 PM
  • This is how app.config files work.  If you have 3 services, you will want them defined in the app.config.  From an administraiton standpoint if you wanted to configure them, you may not know where to look for teh config files.  At least if it is on the config of the exe, it is easy to find and change.

     

    In IIS when you have a different svc file for service and a different web.config because you basically have 3 different wcf service hosts but 3 different service hosting addresses.  In the selfhosting solution you are talking about, there will still be 3 service hosts, but one exe housing it.

     

    Monday, July 07, 2008 1:33 PM

All replies

  • This is how app.config files work.  If you have 3 services, you will want them defined in the app.config.  From an administraiton standpoint if you wanted to configure them, you may not know where to look for teh config files.  At least if it is on the config of the exe, it is easy to find and change.

     

    In IIS when you have a different svc file for service and a different web.config because you basically have 3 different wcf service hosts but 3 different service hosting addresses.  In the selfhosting solution you are talking about, there will still be 3 service hosts, but one exe housing it.

     

    Monday, July 07, 2008 1:33 PM
  • You can load and deserialise client config from a single global location using the custom channel factory:

    Code Snippet

    using System;
    using System.Configuration;
    using System.Reflection;
    using System.Security.Cryptography.X509Certificates;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Configuration;
    using System.ServiceModel.Description;

    namespace WcfService
    {
    /// <summary>
    /// A custom channel factory which takes in a path to a
    /// custom configuration file
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class CustomChannelFactory<T> : ChannelFactory<T>
    {
    string _configurationPath;

    /// <summary>
    /// custom client channel constructor which
    /// specifies an external configuration file
    /// </summary>
    /// <param name="configurationPath"></param>
    public CustomChannelFactory(string configurationPath)
    : base(typeof(T))
    {
    _configurationPath = configurationPath;
    base.InitializeEndpoint((string)null, null);
    }

    /// <summary>
    /// overrides the CreateDescription() method of the channel factory
    /// to apply a new configuration file
    /// </summary>
    /// <returns></returns>
    protected override ServiceEndpoint CreateDescription()
    {
    ServiceEndpoint serviceEndpoint = 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;
    }

    /// <summary>
    /// Configures the binding for the selected endpoint
    /// </summary>
    /// <param name="bindingName"></param>
    /// <param name="group"></param>
    /// <returns></returns>
    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;
    }

    /// <summary>
    /// Adds the configured behavior to the selected endpoint
    /// </summary>
    /// <param name="behaviorConfiguration"></param>
    /// <param name="serviceEndpoint"></param>
    /// <param name="group"></param>
    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);
    }
    }
    }

    /// <summary>
    /// Gets the endpoint identity from the configuration file
    /// </summary>
    /// <param name="element"></param>
    /// <returns></returns>
    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;
    }

    /// <summary>
    /// Helper method to create the right binding depending on the configuration element
    /// </summary>
    /// <param name="configurationElement"></param>
    /// <returns></returns>
    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;
    }
    }
    }



    ...and call it like this:

    CustomChannelFactory<MyService> customChannelFactory = new CustomChannelFactory<MyService>(configFilePath);

    ...this would obviously be in the client code calling the service - but perhaps you could modify it for the actual hosted service?
    Monday, July 07, 2008 3:07 PM
  • Good job. An excellent snippet.
    I have used this, but having looked at the ApplyConfiguration code in ChannelFactory, I thought it was a better "fit" to override ApplyConfiguration rather than CreateDescription. I do NOT call base.ApplyConfiguration but use your code instead. I also added a check for configurationpath==null (I had problems with the base ctor calling InitializeEndpoint for some additional ctor's I created, and with CustomDuplexChannelFactory<> I created) and added the wildcard check as-per the base ApplyConfiguration implementation.

    protected override void ApplyConfiguration(string configurationName)
    {
        if (string.IsNullOrEmpty(_configurationPath))
            return;
        ...
        bool isWildcard = string.Equals(configurationName, "*", StringComparison.Ordinal);
        foreach (ChannelEndpointElement endpoint in serviceModeGroup.Client.Endpoints)
        {
            if (endpoint.Contract == Endpoint.Contract.ConfigurationName && (endpoint.Name == configurationName || wildcard))
            {
                selectedEndpoint = endpoint;
                break;
            }
        }
        ...
    }


    I haven't come across any problem so far, but wonder if you can see anything obviously wrong with my modified approach.
    Cheerz.
    Friday, August 07, 2009 8:13 AM
  • Good job. An excellent snippet.
    I have used this, but having looked at the ApplyConfiguration code in ChannelFactory, I thought it was a better "fit" to override ApplyConfiguration rather than CreateDescription. I do NOT call base.ApplyConfiguration but use your code instead. I also added a check for configurationpath==null (I had problems with the base ctor calling InitializeEndpoint for some additional ctor's I created, and with CustomDuplexChannelFactory<> I created) and added the wildcard check as-per the base ApplyConfiguration implementation.

    protected
     override
     void
     ApplyConfiguration(string
     configurationName)
    {
        if
     (string
    .IsNullOrEmpty(_configurationPath))
            return
    ;
        ...
        bool
     isWildcard = string
    .Equals(configurationName, "*"
    , StringComparison.Ordinal);
        foreach
     (ChannelEndpointElement endpoint in
     serviceModeGroup.Client.Endpoints)
        {
            if
     (endpoint.Contract == Endpoint.Contract.ConfigurationName && (endpoint.Name == configurationName || wildcard))
            {
                selectedEndpoint = endpoint;
                break
    ;
            }
        }
        ...
    }
    


    I haven't come across any problem so far, but wonder if you can see anything obviously wrong with my modified approach.
    Cheerz.


    Can you post the complete class code?
    Thanks
    Wednesday, December 16, 2009 8:05 AM
  • Sorry it's been so long for a reply. The only reason I returned was because I found a slight problem with the original code.

    In LoadBinding, this code:

    IBindingConfigurationElement bindingConfigurationElement = bindingElementCollection.ConfiguredBindings[0];

    assumes we should use the first configuration, but I had 5 different bindings, and I actually wanted the second. So I change the code to:

    IBindingConfigurationElement bindingConfigurationElement = bindingElementCollection.ConfiguredBindings.First(item => item.Name == configurationName);

    My entire code is split across 3 classes. I think it might be slightly easier to post as 3 comments...

    Friday, June 18, 2010 9:09 AM
  • using System.ServiceModel;
    using System.ServiceModel.Description;
    
     /// <summary>
     /// A custom channel factory which takes in a path to a 
     /// custom configuration file
     /// </summary>
     /// <typeparam name="T"></typeparam>
     public class CustomChannelFactory<T> : ChannelFactory<T>
     {
      string _configurationFileName;
    
      /// <summary>
      /// custom client channel constructor which 
      /// specifies an external configuration file
      /// </summary>
      /// <param name="endpointConfigurationName"></param>
      /// <param name="configurationFileName"></param>
      public CustomChannelFactory(string endpointConfigurationName, string configurationFileName)
       : base(typeof(T))
      {
       _configurationFileName = configurationFileName;
       base.InitializeEndpoint(endpointConfigurationName, null);
      }
    
      /// <summary>
      /// overrides the ApplyConfiguration() method of the channel factory
      /// to apply a new configuration file
      /// </summary>
      /// <param name="configurationName"></param>
      protected override void ApplyConfiguration(string configurationName)
      {
       CustomChannelFactoryHelper.ApplyConfiguration(_configurationFileName, Endpoint, configurationName); ;
      }
     }
    

    • Edited by BWakaBats_ Friday, June 18, 2010 9:21 AM
    Friday, June 18, 2010 9:16 AM
  • using System.ServiceModel;
    using System.ServiceModel.Description;
    
     /// <summary>
     /// A custom channel factory which takes in a path to a 
     /// custom configuration file
     /// </summary>
     /// <typeparam name="T"></typeparam>
     public class CustomDuplexChannelFactory<T> : DuplexChannelFactory<T>
     {
      string _configurationFileName;
    
      /// <summary>
      /// custom client channel constructor which 
      /// specifies an external configuration file
      /// </summary>
      /// <param name="callback"></param>
      /// <param name="endpointConfigurationName"></param>
      /// <param name="configurationFileName"></param>
      public CustomDuplexChannelFactory(InstanceContext callback, string endpointConfigurationName, string configurationFileName)
       : base(callback)
      {
       _configurationFileName = configurationFileName;
       base.InitializeEndpoint(endpointConfigurationName, null);
      }
    
      /// <summary>
      /// If you look at that this code actually does in the base implementation,
      /// we have already done this in CreateDescription
      /// </summary>
      /// <param name="configurationName"></param>
      protected override void ApplyConfiguration(string configurationName)
      {
       CustomChannelFactoryHelper.ApplyConfiguration(_configurationFileName, Endpoint, configurationName); ;
      }
     }
    

    • Edited by BWakaBats_ Friday, June 18, 2010 9:20 AM
    Friday, June 18, 2010 9:17 AM
  • using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Globalization;
    using System.Linq;
    using System.Reflection;
    using System.Security.Cryptography.X509Certificates;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Configuration;
    using System.ServiceModel.Description;
    using System.Threading;
    
     static class CustomChannelFactoryHelper
     {
     private static ReaderWriterLock readerWriterLock = new ReaderWriterLock();
     private static Dictionary<string, ServiceModelSectionGroup> groups = new Dictionary<string, ServiceModelSectionGroup>();
    
     public static void ApplyConfiguration(string configurationFileName, ServiceEndpoint serviceEndpoint, string configurationName)
     {
      if (string.IsNullOrEmpty(configurationFileName))
      return;
    
      ServiceModelSectionGroup serviceModeGroup = GetGroup(configurationFileName);
      LoadChannelBehaviors(serviceEndpoint, configurationName, serviceModeGroup); ;
     }
    
     /// <summary>
     /// Load the endpoint with the binding, address, behaviour etc. from the alternative config file 
     /// </summary>
     /// <param name="serviceEndpoint"></param>
     /// <param name="configurationName"></param>
     /// <param name="serviceModeGroup"></param>
     /// <returns></returns>
     public static ServiceEndpoint LoadChannelBehaviors(ServiceEndpoint serviceEndpoint, string configurationName, ServiceModelSectionGroup serviceModeGroup)
     {
      bool isWildcard = string.Equals(configurationName, "*", StringComparison.Ordinal);
      ChannelEndpointElement provider = LookupChannel(serviceModeGroup, configurationName, serviceEndpoint.Contract.ConfigurationName, isWildcard);
    
      if (provider == null)
      return null;
    
      if (serviceEndpoint.Binding == null)
      {
      serviceEndpoint.Binding = LookupBinding(serviceModeGroup, provider.Binding, provider.BindingConfiguration);
      }
    
      if (serviceEndpoint.Address == null)
      {
      serviceEndpoint.Address = new EndpointAddress(provider.Address, LookupIdentity(provider.Identity), provider.Headers.Headers);
      }
    
      if (serviceEndpoint.Behaviors.Count == 0 && !String.IsNullOrEmpty(provider.BehaviorConfiguration))
      {
      LoadBehaviors(serviceModeGroup, provider.BehaviorConfiguration, serviceEndpoint);
      }
    
      serviceEndpoint.Name = provider.Contract;
    
      return serviceEndpoint;
     }
    
     /// <summary>
     /// Load the ServiceModel section fron the config file
     /// </summary>
     /// <param name="configurationFileName"></param>
     /// <returns></returns>
     private static ServiceModelSectionGroup GetGroup(string configurationFileName)
     {
      ServiceModelSectionGroup group;
    
      // Get a read lock while we access the cache collection
      readerWriterLock.AcquireReaderLock(-1);
      try
      {
      // Check to see if we already have a group for the given configuration
      if (groups.TryGetValue(configurationFileName, out group))
      {
       // We found group so return it and we are done
       return group;
      }
      }
      finally
      {
      // always release the lock safely
      readerWriterLock.ReleaseReaderLock();
      }
    
      // if we get here, we couldn't get a group so we need to create one
      // this will involve modifying the collection so we need a write lock
      readerWriterLock.AcquireWriterLock(-1);
      try
      {
      // check an open group wasn't created on another thread while we were
      // acquiring the writer lock
      if (groups.TryGetValue(configurationFileName, out group))
      {
       // we found a group so return it and we are done
       return group;
      }
    
      ExeConfigurationFileMap executionFileMap = new ExeConfigurationFileMap
      {
       ExeConfigFilename = configurationFileName
      };
    
      Configuration config = ConfigurationManager.OpenMappedExeConfiguration(executionFileMap, ConfigurationUserLevel.None);
      group = ServiceModelSectionGroup.GetSectionGroup(config);
    
      // store it in the cache
      groups.Add(configurationFileName, group);
    
      return group;
      }
      finally
      {
      // always release the writer lock!
      readerWriterLock.ReleaseWriterLock();
      }
     }
    
     /// <summary>
     /// Find the endpoint in the alternative config file that matches the required contract and configuration
     /// </summary>
     /// <param name="serviceModeGroup"></param>
     /// <param name="configurationName"></param>
     /// <param name="contractName"></param>
     /// <param name="wildcard"></param>
     /// <returns></returns>
     private static ChannelEndpointElement LookupChannel(ServiceModelSectionGroup serviceModeGroup, string configurationName, string contractName, bool wildcard)
     {
      foreach (ChannelEndpointElement endpoint in serviceModeGroup.Client.Endpoints)
      {
      if (endpoint.Contract == contractName && (endpoint.Name == configurationName || wildcard))
      {
       return endpoint;
      }
      }
      return null;
     }
    
     /// <summary>
     /// Configures the binding for the selected endpoint
     /// </summary>
     /// <param name="group"></param>
     /// <param name="bindingName"></param>
     /// <param name="configurationName"></param>
     /// <returns></returns>
     private static Binding LookupBinding(ServiceModelSectionGroup group, string bindingName, string configurationName)
     {
      BindingCollectionElement bindingElementCollection = group.Bindings[bindingName];
      if (bindingElementCollection.ConfiguredBindings.Count == 0)
      return null;
    
      IBindingConfigurationElement bindingConfigurationElement = bindingElementCollection.ConfiguredBindings.First(item => item.Name == configurationName);
    
      Binding binding = GetBinding(bindingConfigurationElement);
      if (bindingConfigurationElement != null)
      {
      bindingConfigurationElement.ApplyConfiguration(binding);
      }
    
      return binding;
     }
    
     /// <summary>
     /// Gets the endpoint identity from the configuration file
     /// </summary>
     /// <param name="element"></param>
     /// <returns></returns>
     private static EndpointIdentity LookupIdentity(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;
     }
    
     /// <summary>
     /// Adds the configured behavior to the selected endpoint
     /// </summary>
     /// <param name="group"></param>
     /// <param name="behaviorConfiguration"></param>
     /// <param name="serviceEndpoint"></param>
     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Type.InvokeMember", Justification = "This is a real hack, but there is no other way of doing it :(")]
     private static void LoadBehaviors(ServiceModelSectionGroup group, string behaviorConfiguration, ServiceEndpoint serviceEndpoint)
     {
      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,
       CultureInfo.InvariantCulture
       );
      if (extension != null)
      {
       serviceEndpoint.Behaviors.Add((IEndpointBehavior)extension);
      }
      }
     }
    
     /// <summary>
     /// Helper method to create the right binding depending on the configuration element
     /// </summary>
     /// <param name="configurationElement"></param>
     /// <returns></returns>
     private static Binding GetBinding(IBindingConfigurationElement configurationElement)
     {
      if (configurationElement is NetTcpBindingElement)
      return new NetTcpBinding();
    
      if (configurationElement is NetMsmqBindingElement)
      return new NetMsmqBinding();
    
      if (configurationElement is BasicHttpBindingElement)
      return new BasicHttpBinding();
    
      if (configurationElement is NetNamedPipeBindingElement)
      return new NetNamedPipeBinding();
    
      if (configurationElement is NetPeerTcpBindingElement)
      return new NetPeerTcpBinding();
    
      if (configurationElement is WSDualHttpBindingElement)
      return new WSDualHttpBinding();
    
      if (configurationElement is WSHttpBindingElement)
      return new WSHttpBinding();
    
      if (configurationElement is WSFederationHttpBindingElement)
      return new WSFederationHttpBinding();
    
      if (configurationElement is CustomBindingElement)
      return new CustomBinding();
    
      return null;
     }
     }
    

    Friday, June 18, 2010 9:17 AM
  • Thanks so much Dan.

    I had very complex architecture scenario and this proved to be the ultimate solution.


    Mukesh Tank
    Thursday, September 23, 2010 4:11 PM
  • You're right on the average.

    What about when you do an Excel plugin, or a PowerShell extension? You're note going to create/update a Excel.exe.config or PowerShell.exe.config.


    Hello world!
    Friday, April 22, 2011 1:13 PM
  • please note that there is a bug in this code when getting the correct binding.

    This code always get the first binding configuration and this will cause problem when we have multiple bindings in the same binding section.

     

    A revised code is as: 

    1. we need to pass the bindingConfigurationName to the CreateBinding method

    2. Find bindingConfigElement based by config name

     

    in CreateDescription:

    if (selectedEndpoint != null)

                {

                    if (serviceEndpoint.Binding == null &&

                        !string.IsNullOrEmpty(selectedEndpoint.Binding))

                    {

                        serviceEndpoint.Binding = CreateBinding(selectedEndpoint.Binding, selectedEndpoint.BindingConfiguration);

                    }

    }

     

    when CreateBinding: 

    private Binding CreateBinding(string bindingName, string bindingConfigurationName)

            {

                BindingCollectionElement bindingCollection = this.serviceModelSectionGroup.Bindings[bindingName];

     

                if (bindingCollection.ConfiguredBindings.Count == 0)

                {

                    return null;

                }

     

                IBindingConfigurationElement bindingConfigElement;

                if (string.IsNullOrEmpty(bindingConfigurationName))

                {

                    bindingConfigElement = bindingCollection.ConfiguredBindings[0];

                }

                else

                {

                    bindingConfigElement = bindingCollection.ConfiguredBindings.FirstOrDefault<IBindingConfigurationElement>(

                        b => {

                            return b.Name == bindingConfigurationName;

                        });

                }

     

                Binding binding = GetBinding(bindingConfigElement);

                if (bindingConfigElement != null)

                {

                    bindingConfigElement.ApplyConfiguration(binding);

                }

     

                return binding;

            }

     

    And also, as WCF keeping improved from version to version, we need to enhance our IBindingConfigurationElement to Binding mapping when GetBinding...

    :)

    Saturday, August 20, 2011 8:08 AM
  • I have come across this code in several blogs now, and it sounds exactly like what I need. However, I am trying to do this in Silverlight 4/5, but it doesn't appear to like several classes i.e. (System.Configuration, ExeConfigurationFileMap, ServiceModelSectionGroup etc etc).

    How would I do this in Silverlight? I am basically wanting to pass the config in from an external source (this part I have already done), and then apply the servicemodel section to the Silverlight app (this part i am having problems with). 


    Developer
    Wednesday, September 14, 2011 8:47 AM
  • eyeballpaul - Any luck getting this to work in Silverlight? I am trying to do the same thing. I've already implemented this solution in my WinForms applications, but have not been able to in Silverlight.

     

    Thanks

    Monday, October 17, 2011 9:24 PM
  • Hi BWakaBats_ and Yanggu,

    I found your help very useful. Now I have an issue with the code you guys provided and I wondered if you could help me.

    I have a quite complex WCF service. It has behavior extensions and binding element extensions.

    I wonder if the code you wrote reads these settings. I have been writing some code that is similar to LoadBehaviours (LoadExtensions), which looks like this now.

     

            private static void LoadExtensions(ServiceEndpoint serviceEndpoint, string behaviorConfiguration, ServiceModelSectionGroup group)
            {
                ExtensionElementCollection extensionElements = group.Extensions.BindingElementExtensions;
                for (int i = 0; i < extensionElements.Count; i++)
                {
                    ExtensionElement extensionElement = extensionElements[i];
                    object extension = extensionElement.GetType().InvokeMember
                    (
                         "XXX",
                         BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance,
                         null,
                         extensionElement,
                         null,
                         CultureInfo.InvariantCulture
                    );
    
                    if (extension != null)
                    {
                        //
                    }
                }
            }
    
    

     

    Now the problem is: I think I am somewhere in the right direction, but I am missing some pointers. Can you tell me if this is the right approach, and which methods I need to call left?

    Thanks,

     

    Patrick

     

    Tuesday, November 22, 2011 2:40 PM
  • Hello BWakaBats

    I am little new to WCF and I have the same requirement of loading WCF configurations from an external file. Currently I call.

    MyServiceClient serviceClient = new MyServiceClient(); //MyServiceClient is generated by WCF tool (svcutil.exe)

    serviceClient.ProcessDeposit();

    Now I want to know how to use CustomChannelFactory class to load application configuration from an external file.

    Thanks,

    Dimuthu


    • Edited by Dimuthun Monday, November 05, 2012 7:37 PM
    Monday, November 05, 2012 1:57 PM