Wednesday, September 19, 2012

Hosting a Dependency Injected WCF Service

I was having a discussion with a colleague today about how I use my blog as my digital memory or to save keystrokes when helping others. A few moments later, we were debugging a problem that I was convinced I had solved before. Strangely, I have no blog post for this.

So here it is.

Let’s say you’ve got an application and you’d like to expose a part of it on the network. Maybe you want to allow your mobile phone to control your app, or maybe you want two applications to talk to another. There are lots of possibilities here. Within the Microsoft space, one of the best ways to do this is through WCF. From an implementation perspective, it’s all about the ServiceHost.

Let's say the service looks something like this:

using System.ServiceModel;

namespace Example
{
    public class Service : IService
    {
        public string SayHelloTo(string person)
        {
            return String.Format("Hello {0}", person);
        }
    }

    [ServiceContract]
    public interface IService
    {
        [OperationContract]
        string SayHelloTo(string person);
    }
}

And the code to host the service looks something like this:

using System.ServiceModel;

public Example
{

    [TestClass]
    public class ServiceTests
    {
        [TestMethod]
        public void WhenServiceIsHosted_UsingTypeNameAndConfigIsPresent_ShouldConfigureTheServiceWithValuesInTheAppConfig()
        {
            var host = new ServiceHost(typeof(Example.Service));
            
            host.BaseAddresses.Count.ShouldBeGreaterThan(0,
                "The service host was not configured.");
            
            // optionally, if you had to launch it
            // host.Open();
            // Console.ReadLine();
            // host.Close();
        }

    }

}

This all good for basic services. But what if your service is assembled using constructor injection? You won't be able to simply pass the Type to the ServiceHost constructor.

[TestMethod]
public void WhenServiceIsHosted_UsingInstanceAndConfigIsPresent_ShouldConfigureTheServiceWithValuesInTheAppConfig()
{
    Service serviceInstance = ConstructService(); // omitted for clarity
    var host = new ServiceHost(serviceInstance);

    host.BaseAddresses.Count.ShouldBeGreaterThan(0);
}

Only, this test fails! It seems that by passing an instance of our service to the ServiceHost the values in our app.config are not being read.

To correct this issue, the service is configured as singleton and requires us to decorate our concrete implementation of that service with a ServiceBehavior attribute, and configure the InstanceContext for our instance as a Singleton.

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class Service : IService
{
    // ....
}

Bam! Passing tests.

0 comments: