Los Techies : Blogs about software and anything tech!

Integrating StructureMap with WCF


When developing with an IoC container like StructureMap, eventually some place in your code you will need to call the registry to instantiate your classes with their dependencies.  With StructureMap, this means a call to ObjectFactory.GetInstance.  Ideally, we'd like to limit the number of places the registry is called, so that we don't have a lot of StructureMap concerns sprinkled throughout our application.

Suppose we have the following WCF service:

public class CustomerSearchService : ICustomerSearchService
{
    private readonly ICustomerRepository _customerRepository;
    private readonly ICustomerSummaryMapper _customerSummaryMapper;

    public CustomerSearchService(ICustomerRepository customerRepository, ICustomerSummaryMapper customerSummaryMapper)
    {
        _customerRepository = customerRepository;
        _customerSummaryMapper = customerSummaryMapper;
    }

    public CustomerSearchResult FindCustomerByName(string fullName)
    {
        Customer customer = _customerRepository.FindCustomerByName(fullName);

        if (customer == null)
        {
            return new CustomerSearchResult
                       {
                           IsSuccessful = false,
                           FailureReasons = new[] {"Customer not found."}
                       };
        }

        CustomerSummary summary = _customerSummaryMapper.MapFrom(customer);

        return  new CustomerSearchResult {IsSuccessful = true, Result = summary};
    }
}

Nothing too exciting, just a search service that returns customer summary information from a name search.  Now, if we just try to use this service as is, we get a fun exception:

The service type provided could not be loaded as a service because it does not have a default (parameter-less) constructor. To fix the problem, add a default constructor to the type, or pass an instance of the type to the host.

That makes sense, as WCF is in charge of instantiating my service class (CustomerSearchService), but it only knows how to call a no-args constructor.  For a quick fix, we can add this constructor:

public CustomerSearchService()
    : this(ObjectFactory.GetInstance<ICustomerRepository>(), 
        ObjectFactory.GetInstance<ICustomerSummaryMapper>())
{
}

Each of our WCF services would need to the same thing, have a bunch of ObjectFactory calls to set up the dependencies appropriately.  But we can find a better way, and have all of our services wired up automatically, with only one call to StructureMap in the entire application.  To do this, we'll need to plug in to a few WCF extension points to make it happen.

A custom instance provider

WCF provides an interface just for this purpose, IInstanceProvider, that allows us to create custom instantiation behavior.  This interface has basically two methods:

  • CreateInstance - needs to return the right service
  • ReleaseInstance - if we have some custom cleanup to do

Just creating the IInstanceProvider isn't enough, we'll have to tell WCF to use our instance provider, instead of its own default instance provider.  This requires a custom endpoint behavior.  We can't configure the instance provider directly, through configuration or other means.  Instead, we'll use a custom service behavior through the IServiceBehavior interface.  Here's the implementation of our custom service behavior:

public class StructureMapServiceBehavior : IServiceBehavior
{
    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
        {
            ChannelDispatcher cd = cdb as ChannelDispatcher;
            if (cd != null)
            {
                foreach (EndpointDispatcher ed in cd.Endpoints)
                {
                    ed.DispatchRuntime.InstanceProvider = 
                        new StructureMapInstanceProvider(serviceDescription.ServiceType);
                }
            }
        }
    }

    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {
    }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }
}

For all of the endpoints in all of the channels, we need to give WCF our custom instance provider.  Also, since StructureMap needs to know what type to create, we have to pass that information along to our instance provider.  This comes from the ServiceDescription passed in above.

With our service behavior done, let's create the actual instance provider, which will call StructureMap:

public class StructureMapInstanceProvider : IInstanceProvider
{
    private readonly Type _serviceType;

    public StructureMapInstanceProvider(Type serviceType)
    {
        _serviceType = serviceType;
    }

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

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

    public void ReleaseInstance(InstanceContext instanceContext, object instance)
    {
    }
}

Pretty straightforward, we just capture the service type (passed in from our service behavior earlier), then use ObjectFactory to create an instance of that service when asked.  There's no information on the InstanceContext about the service type, or we could have just used it instead.

Now that we have our IInstanceProvider and IServiceBehavior, it's time to hook up this new service behavior to the rest of WCF.  We have a few choices:

  • Attributes
  • Custom service host
  • Configuration

With attributes, we'd decorate all of our services with something like [StructureMapServiceBehavior] or something similar, so that WCF would know to attach our IServiceBehavior to the ServiceDescription behaviors.  Attributes are okay, but again, we'd have to put something on all of our services.  Since the whole point of this exercise was to reduce the footprint, let's go the custom service host route.

As for configuration, I can't stand XML, so let's just pretend that one doesn't exist.

A custom service host

By going the custom ServiceHost route, we'll just need to plug in to the right event to add our custom service behavior to the mix.  Here's what we wind up creating:

public class StructureMapServiceHost : ServiceHost
{
    public StructureMapServiceHost()
    {
    }

    public StructureMapServiceHost(Type serviceType, params Uri[] baseAddresses)
        : base(serviceType, baseAddresses)
    {
    }

    protected override void OnOpening()
    {
        Description.Behaviors.Add(new StructureMapServiceBehavior());
        base.OnOpening();
    }
}

Nothing too exciting, we just add our custom service behavior to the ServiceDescription Behaviors collection, right before the service host is opened in the OnOpening method.

Next, to plug in our custom service host, we'll need a custom service host factory:

public class StructureMapServiceHostFactory : ServiceHostFactory
{
    public StructureMapServiceHostFactory()
    {
        StructureMapConfiguration
            .ScanAssemblies()
            .IncludeTheCallingAssembly()
            .With<DefaultConventionScanner>();
    }

    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
    {
        return new StructureMapServiceHost(serviceType, baseAddresses);
    }
}

The overridden method makes sense, override the CreateServiceHost to...create our custom service host.  However, there's quite a bit going on in the constructor.

At this point, it will start to matter how you'd like StructureMap to be configured:

  • Attributes
  • XML config
  • Code

I'm liking the code way, as we don't have to have crazy XML or a bunch of attributes everywhere.  Which way you configure is up to you, but if you go the code route, you'll need to put the StructureMap configuration in the constructor here.

Finally, we'll need to configure our WCF service to use this service host factory.  I'm using IIS to host, so I'll just need to change my .svc file:

<%@ ServiceHost Language="C#" Debug="true" 
    Service="SMExample.Wcf.CustomerSearchService" 
    Factory="SMExample.Wcf.StructureMapServiceHostFactory" %>

Other hosting solutions will use different ways of using the custom service host factory.

Recap

We wanted to use StructureMap with WCF, but this initially presented us with some problems.  WCF requires a no-args constructor, which means we'll have to use a lot of ObjectFactory.GetInstance calls on every service implementation.

Instead, we can use WCF extension points to use StructureMap to create the service for us, without needing that messy constructor.  We created two extensions:

  • IInstanceProvider
  • IServiceBehavior

Once we had our custom instance provider and service behavior, we needed to decide how our custom service behavior would get attached to our service.  We chose the custom service host route, which meant two more custom WCF implementations:

  • ServiceHost
  • ServiceHostFactory

The ServiceHostFactory implementation forced us to decide how our dependencies should be configured in StructureMap, and we went the code route.  Finally, we configured our service host in IIS to use the right service host factory.

With all this in place, none of our service classes nor their implementations need to know anything about StructureMap, allowing their design to grow and change outside of WCF construction concerns.  We also reduced the footprint of StructureMap in our application, where we now had exactly one call to the StructureMap infrastructure, ObjectFactory.GetInstance.  With this one place StructureMap is used, it will be easier to swap out IoC container implementations (just kidding, Jeremy).

Kick It on DotNetKicks.com
Posted Jul 29 2008, 10:44 PM by bogardj
Filed under: , ,

Comments

Dew Drop - July 30, 2008 | Alvin Ashcraft's Morning Dew wrote Dew Drop - July 30, 2008 | Alvin Ashcraft's Morning Dew
on 07-30-2008 7:13 AM

Pingback from  Dew Drop - July 30, 2008 | Alvin Ashcraft's Morning Dew

Jeremy D. Miller wrote re: Integrating StructureMap with WCF
on 07-30-2008 8:59 AM

Awesome post!  Now, how do you feel about getting that WCF goodness into the next StructureMap release?

Oran wrote re: Integrating StructureMap with WCF
on 07-30-2008 11:13 AM
Darren wrote re: Integrating StructureMap with WCF
on 07-30-2008 12:17 PM

Great post.

What version of StructureMap is this post referencing?  It would help readers if all bloggers would add the versions of tools they are working with in their posts.

FYI, part of the text in this post is being cut off on the right side of page.  I had to make the browser well over 1600 pixels wide (in IE 7 and FF 2) before I could read all the words.

Jimmy Bogard wrote re: Integrating StructureMap with WCF
on 07-30-2008 1:01 PM

@Oran

Yeah, your example, plus a Unity and Windsor example were combined for this one.

@Darren

It's SM 2.4.9.  As for the code, sorry about that!  I'm so used to reading in a feed reader, I forget to check in an actual browser.  I'll make a note of that for future posts.  Thanks!

Ray Henry wrote re: Integrating StructureMap with WCF
on 08-08-2008 1:28 PM

Hey y'all,

For Unity/WCF integration see:

initializecomponent.blogspot.com/.../integrating-unity-with-wcf.html

Thanks!

Brian Walk wrote re: Integrating StructureMap with WCF
on 08-21-2008 6:40 PM

Great Post! I have implemented all of the changes to my app but I am having an issue setting up the default instance in WCF. I have seen posts that setup the interface to a concrete class in code(e.g. a Registry), but I must be missing something when implementing this in WCF, how do you do this in a WCF app?

bogardj wrote re: Integrating StructureMap with WCF
on 08-21-2008 9:49 PM

@Brian

Great question!  Might I suggest putting this question on the StructureMap mailing list?

groups.google.com/.../structuremap-users

We're trying to consolidate the questions and such into one search-able place.

Brian Walk wrote re: Integrating StructureMap with WCF
on 08-22-2008 4:59 AM

Thanks for the suggestion. here is the post

groups.google.com/.../cae6e43a6d05592e

StructureMap Registry in WCF « maonet technotes wrote StructureMap Registry in WCF &laquo; maonet technotes
on 09-05-2008 3:01 PM

Pingback from  StructureMap Registry in WCF &laquo; maonet technotes

StructureMap Registry in WCF « maonet technotes wrote StructureMap Registry in WCF &laquo; maonet technotes
on 09-05-2008 3:01 PM

Pingback from  StructureMap Registry in WCF &laquo; maonet technotes

Jimmy Bogard wrote Some IoC Container guidelines
on 09-12-2008 8:10 AM

So Derick Bailey asked me the other day a few weeks ago to describe how we use our IoC container. I wouldn&#39;t

Jimmy Bogard wrote Integrating StructureMap and NHibernate with WCF
on 09-16-2008 3:47 PM

Following many examples I found online for other IoC containers, I borrowed utilized those designs to

DotNetKicks.com wrote Integrating StructureMap with WCF
on 09-16-2008 4:47 PM

You've been kicked (a good thing) - Trackback from DotNetKicks.com

Implementing a Customer Search Service with DDD, BDD and the Entity Framework « Cav’s Weblog wrote Implementing a Customer Search Service with DDD, BDD and the Entity Framework &laquo; Cav&#8217;s Weblog
on 10-08-2008 6:13 PM

Pingback from  Implementing a Customer Search Service with DDD, BDD and the Entity Framework &laquo; Cav&#8217;s Weblog

User links about "wcf" on iLinkShare wrote User links about "wcf" on iLinkShare
on 10-28-2008 5:18 PM

Pingback from  User links about "wcf" on iLinkShare

FrankM wrote re: Integrating StructureMap with WCF
on 10-29-2008 2:03 PM

The new 2.5 ObjectFactory.Initializa() syntax doesn't work here. It seems ServiceHostFactory doesn't support Lambda expression.

Could you look at it?

FrankM wrote re: Integrating StructureMap with WCF
on 10-30-2008 2:22 PM

Sorry, I have played web.config for a while, this compile error magically went away. It was my machine problem. Nothing to do with LinQ.

Jeremy D. Miller -- The Shade Tree Developer wrote A Gentle Quickstart for StructureMap 2.5
on 11-30-2008 9:57 PM

The most general question I get with StructureMap is “how do I get started?” Personally, I’d recommend

Community Blogs wrote A Gentle Quickstart for StructureMap 2.5
on 11-30-2008 10:49 PM

The most general question I get with StructureMap is “how do I get started?” Personally, I’d recommend

#.think.in wrote #.think.in infoDose #20 (2nd Mar - 6th Mar)
on 03-10-2009 5:44 PM

#.think.in infoDose #20 (2nd Mar - 6th Mar)

TimRayburn.net wrote New Project, New Technologies
on 06-29-2009 3:49 PM

Starting Monday morning I will be starting a new project for a client of my employer Improving Enterprises .  I’ve spent a good deal of time talking with my new teammates about what technologies we will be using for the project, and I thought that

Andy wrote re: Integrating StructureMap with WCF
on 10-10-2009 2:35 PM

This is great.  I was really worried about implementing this but thanks to your help it was very easy.  I did make one change, however.  I'm using a Bootstrapper class as follows:

public class Bootstrapper : IBootstrapper

   {

       public virtual void BootstrapStructureMap()

       {

           ObjectFactory.Initialize(x =>

                {

                    x.ForRequestedType<ITestRespository>().TheDefaultIsConcreteType<TestRespository>();

                    x.ForRequestedType<ITestService>().TheDefaultIsConcreteType<TestService>();

                });

       }

       public static void Bootstrap()

       {

           new Bootstrapper().BootstrapStructureMap();

       }

   }

To use the Bootstrapper, just call it in from the constructor of StructureMapServiceHostFactory:

   public class StructureMapServiceHostFactory : ServiceHostFactory

   {

       public StructureMapServiceHostFactory()

       {

           Bootstrapper.Bootstrap();

       }

       protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)

       {

           return new StructureMapServiceHost(serviceType, baseAddresses);

       }

   }

Derik Whittaker wrote Can anyone spot what is wrong here?
on 10-23-2009 7:40 AM

4 Advil later and my head really hurts.  Here is a little story about how a simple little typo can

Derik Whittaker wrote Custom Service Host for WCF
on 11-23-2009 7:10 AM

In this great post by Jimmy Bogard or this Dimecasts on the subject we take a look at how to add IoC

The big web project: Chapter One « Matthew Butt wrote The big web project: Chapter One &laquo; Matthew Butt
on 12-08-2009 7:19 PM

Pingback from  The big web project: Chapter One «  Matthew Butt

AbstractCode wrote Just because I copied it doesn
on 12-21-2009 8:43 AM

Just because I copied it doesn

Tom Schreck wrote re: Integrating StructureMap with WCF
on 01-18-2010 11:18 AM

I've created all of the code from the examples outlined in the blog and it builds.  I have my SearchService.svc file created that looks like this:

<%@ ServiceHost Language="C#" Debug="true"

   Service="WcfStructureMap.CustomerSearchService"

   Factory="WcfStructureMap.StructureMapServiceHostFactory" %>

I run the service by hitting F5 and I still get the missing default parameterless constructor error message.  I suspect I'm missing a configuration setting in app.config.  How do I wire up the svc file?  I created the file, but not sure how it gets wired into the WCF service.

Is it possible for you to post example code please?

Tom Schreck wrote re: Integrating StructureMap with WCF
on 01-18-2010 12:24 PM

By "post example code" in my previous comment, i meant do you have a download available with example code implementing how StructureMap and Wcf please?  Sorry for the confusion.

bogardj wrote re: Integrating StructureMap with WCF
on 01-18-2010 3:01 PM

@Tom

So...this post is out of date, check this post out with complete code:

andreasohlund.blogspot.com/.../unitofwork-in-wcf-using-structuremap.html

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

Enter the numbers above:
Copyright Los Techies 2008, 2009. All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems