Home > Programming > Unit Testing, WCF and MEF – Part 2

Unit Testing, WCF and MEF – Part 2


In the first part of this article I showed how I’ve been testing code using MEF and a generic base class that is in charge of wiring up the dependencies.

In this part I will focus on how I got WCF to wire up my web service using MEF in what I think is quite an elegant way.

My first requirement was to be able to write a web service that will use dependencies (imports) but that wouldn’t require me to write the composition code in the service class itself. Most of the examples I saw online didn’t provide me with a good way to do that unfortunately.

Until I came across this excellent post by Tim Roberts which shows how to tap into the WCF pipe-line and have your services composed automatically instead of having to do it per service or including wire-up code in your classes.

While the code Tim provided was a great start I thought it wasn’t just right for me. Specifically it was the fact that it required custom code in the Global.asax file which for me was not as elegant as it should be.

So I’ve done a bit more digging into the WCF pipe-line and discovered that I could achieve an elegant, completely configuration-based solution by implementing my own endpoint behavior.

In the end I needed to only create 3 classes to achieve this: EndpointComposedElement, ComposedEndpointBehavior and ComposedInstanceProvider.

By the way, if you’re interested in the WCF pipe-line in general there is a nice diagram here: http://andrewtokeley.net/archive/2008/07/31/wcf-simplified-to-a-single-diagram.aspx.

Now, onwards to the code:

EndpointComposedElement

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Configuration;
using System.ComponentModel.Composition.Primitives;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using System.ServiceModel.Configuration;

namespace SampleApplication.Web.ServiceModel
{
    public class EndpointComposedElement : BehaviorExtensionElement
    {
        [ConfigurationProperty("assemblies")]
        [ConfigurationCollection(typeof(NameValueConfigurationCollection))]
        public NameValueConfigurationCollection ConfigAssemblies
        {
            get
            {
                return (NameValueConfigurationCollection)this["assemblies"];
            }
        }

        public override Type BehaviorType
        {
            get { return typeof(ComposedEndpointBehavior); }
        }

        protected override object CreateBehavior()
        {
            ComposablePartCatalog catalog = null;

            var configTypes = ConfigAssemblies;

            if (configTypes != null)
            {
                var assCatalogs = new List<AssemblyCatalog>(configTypes.Count);

                assCatalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly())); //first add the current assembly

                foreach (NameValueConfigurationElement nvce in configTypes) //add any additional assemblies from the configuration
                {
                    assCatalogs.Add(new AssemblyCatalog(Assembly.Load(nvce.Value)));
                }

                catalog = new AggregateCatalog(assCatalogs);
            }
            else //no configuration, only use the current assembly
            {
                catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            }

            var container = new CompositionContainer(catalog);

            return new ComposedEndpointBehavior(container);
        }
    }
}

This class creates my custom behavior and passes to it the initialized CompositionContainer with the assemblies it found configured(see configuration) and the assembly its running in (GetExecutingAssembly()).

ComposedEndpointBehavior

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Channels;
using System.ComponentModel.Composition.Hosting;

namespace SampleApplication.Web.ServiceModel
{
    public class ComposedEndpointBehavior : IEndpointBehavior
    {        
        private readonly CompositionContainer container;

        public ComposedEndpointBehavior(CompositionContainer container)
        {            
            this.container = container;
        }

        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {}

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {}

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            ComposedInstanceProvider provider = ComposedInstanceProvider.CreateProvider(endpoint.Address.Uri.ToString(), 
                endpoint.Contract.ContractType, container);

            if (provider == null) throw new InvalidOperationException();

            endpointDispatcher.DispatchRuntime.InstanceProvider = provider;                            
        }

        public void Validate(ServiceEndpoint endpoint)
        {}        
    }
}

This custom behavior’s role is to attach the ComposedInstanceProvider to the configured end-point. This behavior class which was initialized by the EndpointComposedElement already received the initialized container and therefore can create the custom Instance Provider with the container it received.

ComposedInstanceProvider

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ServiceModel.Dispatcher;
using System.ComponentModel.Composition.Hosting;
using System.ServiceModel.Channels;
using System.ServiceModel;
using System.ComponentModel.Composition.Primitives;
using System.ComponentModel.Composition;

namespace SampleApplication.Web.ServiceModel
{
    /// <summary>
    /// from: http://www.timjroberts.com/2011/08/wcf-services-with-mef/
    /// </summary>
    public class ComposedInstanceProvider : IInstanceProvider
    {
        private static Dictionary<string, ComposedInstanceProvider> providers = 
                new Dictionary<string, ComposedInstanceProvider>();

        private readonly CompositionContainer container;

        public Type ServiceType { get; private set; }

        private ComposedInstanceProvider(Type serviceType, CompositionContainer container)
        {            
            ServiceType = serviceType;
            this.container = container;
        }

        public static ComposedInstanceProvider CreateProvider(string address, Type serviceType, 
                                        CompositionContainer container)
        {
            ComposedInstanceProvider provider = null;

            if (providers.ContainsKey(address))
            {
                provider = providers[address];
            }
            else
            {
                provider = new ComposedInstanceProvider(serviceType, container);
                providers.Add(address, provider);
            }

            return provider;
        }

        public static ComposedInstanceProvider GetProvider(string address)
        {
            if (providers.ContainsKey(address))
            {
                return providers[address];
            }

            return null;
        }

        public object GetInstance(InstanceContext instanceContext, Message message)
        {            
            return GetInstance(instanceContext);            
        }

        public object GetInstance(InstanceContext instanceContext)
        {            
            Export export = GetServiceExport();

            if (export == null)
            {                
                throw new InvalidOperationException();
            }

            return export.Value;
        }

        public void ReleaseInstance(InstanceContext instanceContext, object instance)
        {
            throw new NotImplementedException();
        }

        private Export GetServiceExport()
        {
            var importDefinition
              = new ContractBasedImportDefinition(AttributedModelServices.GetContractName(ServiceType),
                                        AttributedModelServices.GetTypeIdentity(ServiceType),
                                        null,
                                        ImportCardinality.ZeroOrMore,
                                        true,
                                        true,
                                        CreationPolicy.Any);

            return container.GetExports(importDefinition).FirstOrDefault();
        }
    }
}

Finally, the custom Instance Provider provides the WCF pipe-line with the instance of the service class already initialized and with imports fulfilled because the provider received the initialized composition container from the custom behavior.

Web Service & Configuration

To be able to tie these things together I used the system.serviceModel configuration section like this:

<system.serviceModel>
    <extensions>
      <behaviorExtensions>        
    <add name="endpointComposed" type="SampleApplication.Web.ServiceModel.EndpointComposedElement, SampleApplication.Web"/>
      </behaviorExtensions>
    </extensions>
    <behaviors>
      <endpointBehaviors>
        <behavior name="jsonBehavior">
          <enableWebScript/>
      <endpointComposed>
        <assemblies>
            <add name="sampleApplication" value="SampleApplication"/>
        </assemblies>
          </endpointComposed>
        </behavior>                
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="SampleServiceAspNetAjaxBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug httpHelpPageEnabled="true" includeExceptionDetailInFaults="true"/>         
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service name="SampleApplication.Web.Services.SampleService" behaviorConfiguration="SampleServiceAspNetAjaxBehavior">
        <endpoint address="json" binding="webHttpBinding" behaviorConfiguration="jsonBehavior" contract="SampleApplication.Web.Services.ISampleService"/>
        <endpoint address="" binding="basicHttpBinding" contract="SampleApplication.Web.Services.ISampleService"/>
      </service>
    </services>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
  </system.serviceModel>

First, I declared my custom behavior extension under the <extensions> element. Then I defined the extension’s contents under the <endpointBehaviors>/<behavior>/<endpointComposed> section and provided the assembly name I wish to have used as part of the composition.

Finally I linked all of this behavior to my service’s endpoint using the behaviorConfiguration attribute on the endpoint element.

What of all of this allowed me to do is the ability to create a service class without any kind of wiring code as well as write tests for my service using the same exact process I’ve explained in the first part of this article. I am now able to take this code to existing and new projects alike and not have to write any custom code for the composition anew each time.

Here is my service class:

public class SampleService : ISampleService
{
    [Import]
    private ISampleSentenceService sentenceService;

    public string GetHelloWorld()
    {
        string response = null;

        try
        {
            response = sentenceService.GetSentence();
        }
        catch (Exception ex)
        {
            response = "ERROR!";
        }

        return response;
    }        
}

No composition or wiring code here…

And here is a test I’ve written for it:

[TestClass]
public class SampleServiceIntegrationTest : AutoTestBase
{
    [Import]
    private ISampleService service;

    [TestMethod]
    public void TestGetHelloWorldSuccess()
    {
        string expectedResponse = "Hello World";

        string response = service.GetHelloWorld();

        Assert.IsNotNull(response);
        Assert.AreEqual(expectedResponse, response);
    }
}

Here is the test configuration XML I used for my SampleApplication.Web.Test project:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <section name="testConfiguration" 
        type="SampleApplication.Test.Base.Configuration.TestConfigurationSection, SampleApplication.Test.Base"/>
    </configSections>

    <testConfiguration>
        <Assemblies>
          <add name="sampleapplication" value="SampleApplication"/>
          <add name="sampleapplicationWeb" value="SampleApplication.Web"/>
        </Assemblies>        
    </testConfiguration>
</configuration>

Naturally, The web service itself works just fine and can be accessed by an HTTP request:

gethelloworld

Conclusion

I was very happy to reach my goal of easily creating web services and tests alike without going through the hassle of managing dependencies manually or having to write a lot of wiring code just so my objects can play together.

While I have found this approach to work in several small applications this is not tested in Production or in large scale applications so if you plan on using this please thoroughly test and I’d of course love to hear feedback.

Code

Ive uploaded the code sample to BitBucket here: https://bitbucket.org/yoavniran/wcf-mef-and-testing-sample-code

  1. October 28, 2012 at 13:57 | #1

    Yes! Finally someone writes about wayfair.

  1. No trackbacks yet.

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 315 other followers

%d bloggers like this: