Keep WCF Client Configuration in a Class Library app.config

When building a WCF client with Visual Studio with the simple “Add Service Reference” feature, you get your client configuration (system.serviceModel section) in your app.config file. If the project that holds the config file is an application (Winform, WPF, ASP.NET etc.) then probably you’re just in the demo phase. Usually you have several projects that need to call the service, and you don’t want to add the service references in each. Also, you don’t want to bind all other project to the WCF technology, so you want them to call a library that communicates with the WCF service.

After creating you class library with the service references and app.config file, you can create a layer over the service calls like that:

// Service code
[ServiceContract]
public interface IService1
{
    [OperationContract]
    string GetData(int value);
}

// Client code
public class Service1Agent
{
    public static int GetData(string value)
    {
        using (var client = new Service1Client())
        {
            return client.GetData(value);
        }
    }
}

Now any call to the WCF service is unaware of the WCF technology:

var result = Service1Agent.GetData(5);

When running this simple and “obviously should work” code, you’ll get the following famous exception:

Could not find default endpoint element that references contract ‘Service1.IService1’ in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this contract could be found in the client element.

The reason for that is simple. Class libraries don’t have a configuration file of their own in run-time and they rely on the configuration file of the app they run within. You end up copying the system.serviceModel section to the app.config or web.config of your application and also to your tests project (as said here and here). Although you might accept the duty to do it every time your class library config file gets updated (adding service reference, changing address, changing security etc.), there are scenarios this is not acceptable, for example when you develop your ServiceAgent project for an existing product that you’re not allowed to edit its application config file.

Although this is a very famous issue, there are not many solutions for that (except to copy). Possible solution to the problem you can find here, however the code that generates the client is very complicated. A better solution can be found here, but it assumes that you know the endpoint name and it is hard-coded.

My solution is based on the latter, but is dynamic. Microsoft added a new class in .NET 4 called ConfigurationChannelFactory. This class allows you to create a channel with custom configuration source, for example:

var channelFactory = new ConfigurationChannelFactory<IService1Channel>(endpointName, configuration, null);
var client = channelFactory.CreateChannel();

var result = client.GetData(5);

For the above code to work, you need to load your configuration and know the endpoint name. Loading the configuration from file is easy with the ConfigurationManager.OpenMappedExeConfiguration method:

var configuration = ConfigurationManager.OpenMappedExeConfiguration(new ExeConfigurationFileMap { ExeConfigFilename = "app.config" }, ConfigurationUserLevel.None);

The app.config file is the class library config file. You can have it copied to the application output directory by setting “Copy To Output Directory” to “Copy always” in the file properties.

As for the endpoint name, in order not to make a switch-case for all service referenceד you got, you need to find it in the config file. In the config file the endpoint may appear like that:

<endpoint address="http://localhost:12345/Service1.svc" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService1" contract="Service1.IService1" name="BasicHttpBinding_IService1" />

Knowing the contract name can help you find the endpoint name. The interface hierarchy in the generated client code is:

With an instance of IService1Channel you can call the service and close connection since it is IDisposable, so this is the type you should pass as the generic T parameter to the ConfigurationChannelFactory constructor.

The interface IService1 has an attribute with the ConfigurationName that corresponds to the contract attribute value in the config file:

[System.ServiceModel.ServiceContractAttribute(ConfigurationName="Service1.IService1")]
public interface IService1
{
}

In order to find this attribute we can find the interface that IService1Channel inherits from and find the attribute with GetCustomAttributes:

var channelType = typeof(T);
var contractType = channelType.GetInterfaces().First(i => i.Namespace == channelType.Namespace);
// We compare the namespace because the inteface inherits also from System.ServiceModel.IClientChannel
var contractAttribute = contractType.GetCustomAttributes(typeof(ServiceContractAttribute), false).First() as ServiceContractAttribute;

With the configuration and the contract name you can find the endpoint:

var serviceModelSectionGroup = ServiceModelSectionGroup.GetSectionGroup(configuration);
var endpoint = serviceModelSectionGroup.Client.Endpoints.OfType<ChannelEndpointElement>().First(e => e.Contract == contractAttribute.ConfigurationName);

All above code is making the complete client generation helper method:

public static T GetClient<T>() where T : IClientChannel
{
    var channelType = typeof(T);
    var contractType = channelType.GetInterfaces().First(i => i.Namespace == channelType.Namespace);
    var contractAttribute = contractType.GetCustomAttributes(typeof(ServiceContractAttribute), false).First() as ServiceContractAttribute;

    var configuration = ConfigurationManager.OpenMappedExeConfiguration(new ExeConfigurationFileMap { ExeConfigFilename = "app.config" }, ConfigurationUserLevel.None);
    var serviceModelSectionGroup = ServiceModelSectionGroup.GetSectionGroup(configuration);
    var endpoint = serviceModelSectionGroup.Client.Endpoints.OfType<ChannelEndpointElement>().First(e => e.Contract == contractAttribute.ConfigurationName);

    var channelFactory = new ConfigurationChannelFactory<T>(endpoint.Name, configuration, null);
    var client = channelFactory.CreateChannel();

    return client;
}

With this helper method you can write your ServiceAgent like that:

public static string GetData(int value)
{
    using (var client = ClientHelper.GetClient<IService1Channel>())
    {
        return client.GetData(value);
    }
}

With this code you have achieved abstraction over the WCF technology and you no longer need to inject XML into the application config file as long as you library app.config is in the application bin folder.

Advertisements
This entry was posted in Configuration, WCF and tagged , , , . Bookmark the permalink.

3 Responses to Keep WCF Client Configuration in a Class Library app.config

  1. onemenny says:

    Hi Amiram,

    I read your post and was confused. While I understand theneed to have a separated class library with services and consume it asstandalone library, I don’t understand the need for the endpoint to beconfigured in the application consuming your class library and not by the classlibrary itself. The class library can be self-hosted or open up endpoints byits own if I’m not mistaking…

    The other thing, is your code relays on the”first” interface “channelType.GetInterfaces().First” whichis dangerous implementation. You should implement your own attribute and lookfor the interface by attribute instead of the programmer writing order. Samegoes for your custom attribute with the “first” linq method. Youshould use FirstOrDefault and look for nulls – but all of that is “smallmoney”.

    You can skip the “copy local” and use the app.configas embededed resource and then load the configuration from your embeddedresource – you still have the disadvantage that no one can change config atruntime (but then again, you want your class library to be 1 consumable unite).

    This is just my thoughts and not criticism or something. Thankyou very much for the post. I didn’t know that!

    • Thanks for the feedback.
      About the endpoint configuration place, the WCF auto-generated client code is searching the endpoint config in the application config file. There is no simple way to avoid that unless you’re not using the auto generated code, but many people do. When you use the class library to send something to service, the class library is looking in the application config file.

      About the “First”, all this post is correct if you use the generated code as is, and this way it is completely safe to use “First” because the code structure is fixed and known. If I used FirstOrDefault, what would I do if I get null? anyway I had to throw an exception.

      About the embedded resource, the answer is in your words “no one can change config at runtime”. Yes, I want the class library to be a consumable unit but still I want to give an option to change configuration like address, security etc which are application specific and can’t be determined by the library, otherwise I would have hardcoded this.

      • onemenny says:

        I see your point. Most of my WCF experience is without using the auto generated feature… in that way you are really abstracting the layer and able to manipulate the configuration as you wish.

        None the less, when you add more interfaces and complex your code further – it’s not a good practice to call “First” unless you are 100% sure you’ll get a result. It’s better to call “FirstOrDefault” and check for nulls IMHO.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s