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(endpointName, configuration, null);
var client = channelFactory.CreateChannel();
var result = client.GetData(5);</pre>

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:

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().First(e => e.Contract == contractAttribute.ConfigurationName);</pre>

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

public static T GetClient() 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()
                          .First(e = e.Contract == contractAttribute.ConfigurationName);
	var channelFactory = new ConfigurationChannelFactory(endpoint.Name, configuration, null);
	var client = channelFactory.CreateChannel();
	
	return client;
}</pre>

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

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

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.

Thank you for your interest!

We will contact you as soon as possible.

Send us a message

Oops, something went wrong
Please try again or contact us by email at info@tikalk.com