Pablo M. Cibraro (aka Cibrax)

My thoughts on Web Services and .NET development

News

Pablo Cibraro's VisualCV

Blogs

Personal

Programming

Loading the WCF configuration from different files on the client side

A common problem in WCF that many people face is the impossibility of loading the client configuration from different configuration files. This is a common scenario  when the developer wants to deploy some binaries with along with an independent configuration file (Which may be in a resource file also) to avoid modifying the main configuration file. A weeks ago, I described a easy workaround to use the external configuration files through the use of the configSource attribute (A mechanism provided by .NET). However, that approach requires several configuration files to configure an entire WCF channel, at least three files (client section, bindings and behaviors).

Another approach, which is less common and I will describe in this post, requires some custom code to extend the ChannelFactory<T> class.

The ChannelFactory<T> provides a virtual method "CreateDescription" that can be overridden to create a ServiceEndpoint.

 

//

// Summary:

//    Creates a description of the service endpoint.

//

// Returns:

//    The System.ServiceModel.Description.ServiceEndpoint of the service.

//

// Exceptions:

//   System.InvalidOperatorException:

//    The callback contract is null but the service endpoint requires one that

//    is non-null.

protected override ServiceEndpoint CreateDescription();

The default implementation of this method basically tries to load the endpoint configuration from the default configuration file (The file configured for the default AppDomain). So, the workaround here is to override that method and load the configuration from another file. It seems to be pretty easy to do at first glance, but as you will see next, it requires a lot of plumbing code to make it work.

The first step is to derive our custom channel class from the ChannelFactory<T> class and add an argument in the constructor to specify a different configuration file.

/// <summary>

/// Custom client channel. Allows to specify a different configuration file

/// </summary>

/// <typeparam name="T"></typeparam>

public class CustomClientChannel<T> : ChannelFactory<T>

{

  string configurationPath;

 

  /// <summary>

  /// Constructor

  /// </summary>

  /// <param name="configurationPath"></param>

  public CustomClientChannel(string configurationPath) : base(typeof(T))

  {

    this.configurationPath = configurationPath;

    base.InitializeEndpoint((string)null, null);

  }

As you can see, a call to the method InitialiazeEndpoint of the base class is required. That method will automatically call to our CreateDescription method to configure the service endpoint.

/// <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;

 

}

 

CreateDescription is the core method where we create the service endpoint using the configuration file specified in the constructor method.

And the last step, is to use our custom channel in the client application instead of the base channel provided by WCF.

CustomClientChannel<ICalculator> channel = new CustomClientChannel<ICalculator>("OtherConfig.config");

ICalculator client = channel.CreateChannel();

The code below uses the ICalculator contract  that comes with the WCF sdk samples.

You can download the complete sample here

 

 

 

Posted: Oct 19 2007, 09:41 AM by cibrax | with 27 comment(s)
Filed under: ,

Comments

Christopher Steen said:

Link Listing - October 19, 2007

# October 19, 2007 11:36 PM

Diario di Bordo said:

Ottimo post di Pablo "Cibrax" Cibraroche spiega come creare un client in grado di leggere la configurazione

# October 22, 2007 9:06 AM

Eddie Chu said:

Nice approach to create the client channel by loading a non-default application configuration file!

However, I just found a tiny problem from your sample that if the endpointConfigurationName is explicitly specified, an InvalidOperationException will be thrown to tell me that the specified endpoint element cannot be found. Any ideas?

# October 25, 2007 2:28 AM

cibrax said:

Hi Eddie, I am not sure about that problem, it must  be bug in code since I did not test it very well for all scenarios. I will take a look more in detail, thanks for the feedback!!.

# October 25, 2007 8:51 AM

cibrax said:

I just uploaded a new version of this code that fixes the non-default configurationName bug.

Thanks

Pablo.

# December 5, 2007 3:58 PM

Timur Fanshteyn said:

One more small bug.

In AddBehaviours() function. If number of EndpointBehaviors is 0, the function will fail.

I had to add

if (group.Behaviors.EndpointBehaviors.Count == 0)

               return;

at the beginning of the function to solve the issue.

----

Thanks a lot for the functionality. Works great.

# December 7, 2007 5:07 PM

Patricio Ogaz D. said:

Nice article, a excelent way to solved this !!!

I use this code for testing porpouse, witah a standard service, but I receive a Exception in the CreateDescription() method.

Then I debug and retouch a line for the call to AddBehaviors , I adding this code at the end of the conodition:

&& selectedEndpoint.BehaviorConfiguration != "")

Because when the behavior isn't set, the behavior it's empty, then the app try to add the configuration and trhow a exception.

The article it's very good and usefull, thanks.

Patricio.

# December 11, 2007 8:30 AM

BlogServiceHost.Create() said:

Qualche tempo fa avevo letto questo post di Pablo Cibraro (aka Cibrax) e mi ero ripromesso di fare la

# December 15, 2007 9:56 AM

BlogServiceHost.Create() said:

Qualche tempo fa avevo letto questo post di Pablo Cibraro (aka Cibrax) e mi ero ripromesso di fare la

# December 15, 2007 12:16 PM

eprystupa said:

Is the source code for the sample still available? The posted URL seems to be down...

Thanks!

-Eugene

# February 12, 2008 10:21 AM

naziya said:

Extremely useful information. With some minor changes I could use this solution to make a C++ client to talk with WCF Server

# April 17, 2008 4:55 AM

Gavin S said:

You rock man....  the WCF team needs to solve this limitation..  I'm writing addons, and can't modify the applications config file.. thanks a ton!

# April 28, 2008 12:25 PM

Kevan Brewer said:

Excellent! Thanks for that, I'm implementing a plug-in component that uses WCF and the main exe does not need to know anything about the endpoint or even WCF for that matter.

I find it hard to believe that this isn't standard functionality in WCF!

I had to modify the CreateDescription() method to cope with no 'Behavior' entries as follows:

if (serviceEndpoint.Behaviors.Count == 0 && !string.IsNullOrEmpty(selectedEndpoint.BehaviorConfiguration))

{

   AddBehaviors(selectedEndpoint.BehaviorConfiguration, serviceEndpoint, group);

}

# May 30, 2008 9:48 AM

Leonardo said:

Hi. First of all, great work!

Can you tell me how can I close the channel using this approach?

The service interface doesn't have the close() method.

Thanks,

Leonardo.

# August 11, 2008 10:42 AM

Myxamopiyc said:

Excellent!

Thanks!

I added:

1) if (group.Behaviors.EndpointBehaviors.ContainsKey(behaviorConfiguration)){ ...

2) private Binding GetBinding(string bindingName){switch(bindingName.ToLower())...case "wshttpbinding":return new WSHttpBinding);...}}

and call it from

private Binding CreateBinding(string bindingName, ServiceModelSectionGroup group)

# October 29, 2008 2:13 PM

George said:

Is it possible to extend the DuplexChannelFactory<T> in a similar way? I tried it but this didn't work as I can't assign the callBackObject anywhere in a protected/public property/method.

# November 18, 2008 6:58 PM

Ram said:

This is fantastic article . This code i slightly changed to load from GAC. In my project i faced one issue.I am writing one feature in a class library which is consuming WCF.I could n't load the Config file since my dll is Gaced. With this code i achieved the solution thanks..

# February 6, 2009 4:28 AM

Chris Rothery said:

Thank you, this is awesome.

I couldn't have my own config attached to the main application so this has saved my life (not literally..!)

# February 6, 2009 12:55 PM

Steve said:

Thanks so much!  I have been searching for hours and came across this.  As with one of the commenters above I had to add a check for when there are 0 behaviors, but after that it worked fine.

# February 20, 2009 2:13 PM

fayssal said:

Hi,

Thanks for the code!

I have a problem with the length of arrays allowed from the client when using this code. Apparently the creation of the binding does not parse the all the attributes correctly.

Regards.

# March 30, 2009 4:57 AM

Duke said:

Amazing code! many thanks!

# March 30, 2009 11:33 AM

Nima said:

There's a new API for this in the new .NET 4.0 preview. Class full name: System.ServiceModel.Configuration.ConfigurationChannelFactory.

# May 15, 2009 3:26 PM

smartinz's blog said:

Invocare wcf services da un LoadTest plugin

# May 25, 2009 8:09 AM

Adriana said:

Hi,

Thanks for your post, helped me a lot. I found another bug tho, besides the one with Behaviour. If in the config file are multiple endpoints each one with its binding, it takes always the first binding no matter of the endpoint.

Fixed it like this:

//in CreateDescription() modify

if (serviceEndpoint.Binding == null)

               {

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

               }

...

  if (serviceEndpoint.Behaviors.Count == 0 && !String.IsNullOrEmpty(selectedEndpoint.BehaviorConfiguration))

               {

                   AddBehaviors(selectedEndpoint.BehaviorConfiguration, serviceEndpoint, serviceModeGroup);

               }

  /// <summary>

       /// Configures the binding for the selected endpoint

       /// </summary>

       /// <param name="bindingName"></param>

       /// <param name="group"></param>

       /// <returns></returns>

       private Binding CreateBinding(string bindingName, string bindingConfiguration, ServiceModelSectionGroup group)

       {

           IBindingConfigurationElement be = null;

           BindingCollectionElement bindingElementCollection = group.Bindings[bindingName];

           if (bindingElementCollection.ConfiguredBindings.Count > 0)

           {

               foreach (IBindingConfigurationElement bindingElem in bindingElementCollection.ConfiguredBindings)

               {

                   if (string.Compare(bindingElem.Name, bindingConfiguration) == 0)

                   {

                       be = bindingElem;

                       break;

                   }

               }

               Binding binding = null;

               if (be != null)

               {

                   binding = GetBinding(be);

                   be.ApplyConfiguration(binding);

               }

               return binding;

           }

           return null;

       }

# June 12, 2009 6:22 AM

Pablo M. Cibraro (aka Cibrax) said:

As Dr Nick announced in this post , WCF 4.0 will ship with a new feature to configure a client channel

# September 8, 2009 11:17 AM

SeeSharp said:

WCF Shortcomings and Dark Sides

# September 17, 2009 10:32 AM

Ivan Krivyakov » WCF Configuration: Thou Shalt Not Hard Code said:

Pingback from  Ivan Krivyakov &raquo; WCF Configuration: Thou Shalt Not Hard Code

# October 16, 2009 11:41 PM
Leave a Comment

(required) 

(required) 

(optional)

(required)