Integrating MediatR with Web API

One of the design goals I had in mind with MediatR was to limit the 3rd party dependencies (and work) needed to integrate MediatR. To do so, I only take a dependency on CommonServiceLocator. In MediatR, I need to resolve instances of request/notification handlers. Rather than build my own factory class that others would need to implement, I lean on CSL to define this interface:

public interface IServiceLocator : IServiceProvider
{
    object GetInstance(Type serviceType);
    object GetInstance(Type serviceType, string key);
    IEnumerable<object> GetAllInstances(Type serviceType);
    TService GetInstance<TService>();
    TService GetInstance<TService>(string key);
    IEnumerable<TService> GetAllInstances<TService>();
}

But that wasn’t quite enough. I also wanted to support child/nested containers, which meant I didn’t want a single instance of the IServiceLocator. Typically, when you want a component’s lifetime decided by a consumer, you depend on Func<Foo>. It turns out though that CSL already defines a delegate to provide a service locator, aptly named ServiceLocatorProvider:

public delegate IServiceLocator ServiceLocatorProvider();

In resolving handlers, I execute the delegate to get an instance of an IServiceLocatorProvider and off we go. I much prefer this approach than defining my own yet-another-factory-interface for people to implement. Just not worth it. As a consumer, you will need to supply this delegate to the mediator.

I’ll show an example using StructureMap. The first thing I do is add a NuGet dependency to the Web API IoC shim for StructureMap:

Install-Package StructureMap.WebApi2

This will also bring in the CommonServiceLocator dependency and some files to shim with Web API:

image

I have the basic building blocks for what I need in order to have a Web API project using StructureMap. The next piece is to configure the DefaultRegistry to include handlers in scanning:

public DefaultRegistry() {
    Scan(
        scan => {
            scan.TheCallingAssembly();
            scan.AssemblyContainingType<PingHandler>();
            scan.WithDefaultConventions();
			scan.With(new ControllerConvention());
            scan.AddAllTypesOf(typeof(IRequestHandler<,>));
            scan.AddAllTypesOf(typeof(IAsyncRequestHandler<,>));
            scan.AddAllTypesOf(typeof(INotificationHandler<>));
            scan.AddAllTypesOf(typeof(IAsyncNotificationHandler<>));
        });
    For<IMediator>().Use<Mediator>();
}

This is pretty much the same code you’d find in any of the samples in the MediatR project. The final piece is to hook up the dependency resolver delegate, ServiceLocatorProvider. Since most/all containers have implementations of the IServiceLocator, it’s really about finding the place where the underlying code creates one of these IServiceLocator implementations and supplies it to the infrastructure. In my case, there’s the Web API IDependencyResolver implementation:

public IDependencyScope BeginScope()
{
    IContainer child = this.Container.GetNestedContainer();
    return new StructureMapWebApiDependencyResolver(child);
}

I modify this to use the current nested container and attach the resolver to this:

public IDependencyScope BeginScope()
{
    var resolver = new StructureMapWebApiDependencyResolver(CurrentNestedContainer);

    ServiceLocatorProvider provider = () => resolver;

    CurrentNestedContainer.Configure(cfg => cfg.For<ServiceLocatorProvider>().Use(provider));
    
    return resolver;
}

This is also the location where I’ll attach per-request dependencies (NHibernate, EF etc.). Finally, I can use a mediator in a controller:

public class ValuesController : ApiController
{
    private readonly IMediator _mediator;

    public ValuesController(IMediator mediator)
    {
        _mediator = mediator;
    }

    // GET api/values
    public IEnumerable<string> Get()
    {
        var result = _mediator.Send(new Ping());

        return new string[] { result.Message };
    }

That’s pretty much it. How you need to configure the mediator in your application might be different, but the gist of the means is to configure the ServiceLocatorProvider delegate dependency to return the “thing that the framework uses for IServiceLocator”. What that is depends on your context, and unfortunately changes based on every framework out there.

In my example above, I’m preferring to configure the IServiceLocator instance to be the same instance as the IDependencyScope instance, so that any handler instantiated is from the same composition root/nested container as whatever instantiated my controller.

See, containers are easy, right?

(crickets)

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in MediatR. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Will this work with Windsor?

  • Danny

    Jimmy,

    In my API controllers I see alot of API specific actions such as setting custom headers (pagination meta data headers for instance), returning appropriate HTTP status codes (404 when the resource could not be found, 403 when access to a resource is forbidden, sometimes a 400 (bad request)), etc…

    I understand you can factor authorisation checks out of your handlers, but for access checks this is more problematic because the easiest, most performant and most safe place is inline with your controller code.

    Would you advice to just throw exceptions in your handler and have your ‘API layer’ convert them to the appropriate HTTP response?

    • GuyHarwood

      we implement a decorator for this and have the IOC container decorate each handler. cross cutting concerns etc…

      You can read more about that type of approach here https://cuttingedge.it/blogs/steven/pivot/entry.php?id=91

      it doesn’t use authorisation as an example, but the approach is just the same

      • Danny

        Decorators are a great solution when you are indeed dealing with cross-cutting concerns: transactions, logging, general authorisation, etc…

        But in the case of my access checks:
        1. I don’t want to separate the checking of the access from my handler for safety reasons. It is easy to forget you might need additional access checks when changing what a handler does.

        2. I don’t want to fetch entities multiple times from my database for performance reasons. My handler tries to load entities as performant as possible (as to reduce lazy loading & N+1 problems, etc…) A possible access check decorator doesn’t know this so effectively I have to fetch the same object 2 times; 1 time for the access check and 1 time for the handler logic… A cache isn’t going to help me (since EF doesn’t support it)

        As an example, my code looks like this:

        var aggrageRoot = _repository.FindById(rootId).Include(x => x.SomeEntity).Include(x => x.SomeCollection)

        if (aggregateRoot == null) { // 404 NOT FOUND }

        var someObj = aggregateRoot.SomeCollection.SingleOrDefault(x => x.Id == id)

        if (someObj == null) { // 404 NOT FOUND }

        if (!CurrentUser.CanAccess(someObj)) { // 403 FORBIDDEN }

        // meat of the handler here

        • GuyHarwood

          point 1 – then it belongs in the handler
          point 2 – you could have an outer decorator that checks the cache for the object before loading from db. It will only reach that decorator if the inner auth decorator ran successfully.

  • hope it’s good news for us

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1782()

  • Sam

    We used this for AutoFac

    builder.RegisterAssemblyTypes(typeof(IMediator).Assembly).AsImplementedInterfaces();

    var container = new Lazy(() => builder.Build());

    var resolver = new Lazy(() => new AutofacWebApiDependencyResolver(container.Value));

  • Ayesha Ahmed

    All friends if you are geting bored then i have a very nice chat room where you can find pakistani girls, Indian Girls, Uae Girls, Uk Girls, Adult Girls, Dating Girls, Canada Girls, Islambad Grisl, Multan Girls Chat Room, If You Wana Join This Chat Room Then You Do Not Need Any Typ Of Registration Just Enter Your Nick And Enjoy A Pakistani Chat Rooms

  • Allen Firth

    Jimmy, when will MediatR 2 come out of pre-release on nuget? Please please please!!!

    • jbogard

      Not my choice, when .NET Core comes out of pre-release