21 November 2007

Writing WCF config files programmatically and dynamically

OK, so I said I'd write a series of posts on WPF first, but this came up the other day.

Problem

Suppose you need to write a wizard-type application where you allow a user to expose a service over WCF (maybe allowing the input of a port number and name). At the same time, you want to allow so-called power-users to just edit the app.config file and change WCF details, such as various binding parameters. (The same mechanism applies for the client side.)

So, what you need is to write a WCF configuration, let's say exposing a service, on the fly to the app.config file, and then opening a ServiceHost that uses the configuration that was just written.

Solution


Let's start of with a very simple example. First, the service contract.


[ServiceContract]
internal interface ISimpleService
{

[OperationContract]
string AddWordToString( string input );

}


Now, a service that implements this contract:


[ServiceBehavior( InstanceContextMode=InstanceContextMode.Single )]
public class SimpleService : ISimpleService
{

#region ISimpleService Members

public string AddWordToString( string input )
{
return input + " and a word";
}

#endregion
}


So, what we want to do now is, in code, expose a SimpleService over WCF, while updating the app.config file so that it shows the configuration of the exposed service.

So, the idea is to first write to the WCF config file, and then use a standard ServiceHost call on the service type to allow WCF to read the configuration from app.config, and expose the service. The following code snippet achieves this:



//first write the service config for the service
WriteWCFConfig( typeof(SimpleService), typeof(ISimpleService) );

//now try to open the wcf service
ServiceHost host = new ServiceHost( new SimpleService() );
host.Open();


For the WriteWCFConfig method (of course, the crux of the matter), I recommend reading this blog post, which gives an overview of the object model for the WCF configuration sections (which is basically an implementation of System.Configuration).


private static int portNumber = 12000;

private static void WriteWCFConfig(Type serviceType, Type contractType)
{
//standard method from System.Configuration
Configuration appConfig = ConfigurationManager.OpenExeConfiguration( ConfigurationUserLevel.None );

//the main section in the app.config file for WCF
ServiceModelSectionGroup serviceModel = ServiceModelSectionGroup.GetSectionGroup( appConfig );

//the section
ServiceElementCollection serviceSection = serviceModel.Services.Services;

//a new configuration for a service
ServiceElement newService = new ServiceElement( serviceType.FullName );

if ( !serviceSection.ContainsKey( serviceType.FullName ) )
{
ServiceEndpointElement endpoint
= new ServiceEndpointElement( new Uri( "net.tcp://localhost:" + portNumber ), contractType.FullName );
portNumber++;

endpoint.Binding = "netTcpBinding";
newService.Endpoints.Add( endpoint );

serviceSection.Add( newService );

appConfig.Save();
}
}


THe code above basically uses the System.Configuration API to get an object model for the exising app.config file, uses the API in System.ServiceModel.Configuration to build up the necessary configuration section for a new service, and then saves the config file back to disk. Once this is done, WCF can read the config file, and the code snippet above will work.

However, there is one catch: suppose that, after writing a config and opening a service using ServiceHost, you want to add a new service, like this:


//first write the service config for the service
WriteWCFConfig( typeof(SimpleService), typeof(ISimpleService) );

//now try to open the wcf service
ServiceHost host = new ServiceHost( new SimpleService() );
host.Open();

//do this again with another service
WriteWCFConfig( typeof( AnotherSimpleService ), typeof( ISimpleService ) );

ServiceHost host2 = new ServiceHost( new AnotherSimpleService() );
host2.Open();


This won't work, because WCF (or rather, System.Configuration which is used by WCF to read the app.config file) only reads the app.config file once, and then uses its cache. However, you can force System.Configuration to re-read a certain section of the app.config file from disk, using:


ConfigurationManager.RefreshSection( serviceModel.Services.SectionInformation.SectionName );


Add this line to the obvious place in WriteWCFConfig, and all will work as planned. For the client side, an identical strategy can be used.

11 November 2007

First post

Hello world, A first blog post, in what I hope to be a long series. The main topic of this blog is probably going to be .NET and related technologies, since I'm working with those daily. In time, I'd also like to blog about some more research oriented topics, most notably languages like Haskell and Spec#. Finally, I'm also particularly interested in the topic of software architecture. Hope you will enjoy my blog...