Search code examples
.net.net-coreautofacmediatr

Integrate Mediatr with Autofac


I'm using .NET Core (note: not a web environment). I need to integrate MediatR and Autofac.

The wiki has a guide and StructureMap example. Also, an Autofac sample, which I've simplified. There's no mention of lifetime scopes, but in a separate project, I found useful comments and converted them to Autofac. There are lots of code samples floating around, and each is different.

What I've got so far:

// enables contravariant Resolve() for interfaces with single contravariant ("in") arg
builder
  .RegisterSource(new ContravariantRegistrationSource());

// mediator itself
builder
  .RegisterType<Mediator>()
  .As<IMediator>()
  .InstancePerLifetimeScope();

// request handlers
builder
  .Register<SingleInstanceFactory>(context => {
    var ctx = context.Resolve<IComponentContext>();   // unsure why needed, but it works
    return t => { object o; return ctx.TryResolve(t, out o) ? o : null; };
  })
  .InstancePerLifetimeScope();

// notification handlers
builder
  .Register<MultiInstanceFactory>(context => {
    var ctx = context.Resolve<IComponentContext>();   // unsure why needed, but it works
    return t => (IEnumerable<object>)ctx.Resolve(typeof(IEnumerable<>).MakeGenericType(t));
  })
  .InstancePerLifetimeScope();

Then register my custom stuff:

  • handlers as transient, i.e. InstancePerDependency()
  • pre/post-processors as per-request, i.e. InstancePerLifetimeScope()
  • behaviors as transient, i.e. InstancePerDependency()

For e.g.

builder.RegisterType<EditCommandHandler>().AsImplementedInterfaces().InstancePerDependency();

However in most code samples I've seen, especially for StructureMap, there are loads of other registrations (just for e.g. IRequestHandler<,>, IRequestHandler<>, IAsyncRequestHandler<,>, IAsyncRequestHandler<>, ICancellableAsyncRequestHandler<,>, ICancellableAsyncRequestHandler<>, INotificationHandler<>, IAsyncNotificationHandler<>, ICancellableAsyncNotificationHandler<>), and the ASP.NET Core container's integration is extremely complex too. I haven't done anything similar.

If you have this working in production, how does your config compare to mine? Thanks!


Solution

  • TL;DR;

    I think your registrations are fine.


    Tell me more - aka let's take your questions one by one

    Your current registrations

    MediatR needs you to register SingleInstanceFactory and MultiInstanceFactory as they are needed for the Mediator class.

    Then you need to register your handlers and behaviors, which you do, so you're fine.

    One comment I could make is that, looking at your example, it looks like you register your handlers one by one. While this works - and this is the most important, it means that every time you add a new request/handler pair, you have to register the handler in the Autofac container.

    I'd suggest moving to a convention-based registration style, which Autofac supports out of the box. Let's say your application uses async request handlers and async notification handlers, yo ucould write something like:

    var openHandlersTypes = new[] { typeof(IAsyncRequestHandler<,>), typeof(IAsyncNotificationHandler<,>) };
    foreach (var openHandlerType in openHandlersTypes)
    {
        builder
            .RegisterAssemblyTypes(ThisAssembly)
            .AsClosedTypesOf(openHandlerType)
            .InstancePerDependency();
    }
    

    Autofac will scan ThisAssembly and register all the types that are closing the open generic types present in openHandlersTypes.

    About all the handlers types

    You're worried that you don't have registrations for all the handlers types that MediatR exposes. That's perfectly fine. If you don't use cancellable handlers, no need to register them. You get to choose what type of handler you implement. When you send a request to MediatR, it will use the provided MultiInstanceFactory to figure out if your handler is a synchronous one, an asynchronous one, or a cancellable asynchronous.

    Also, please note that all request handler types come in two flavors. The ones that have only one generic type like IRequestHandler<TRequest> do not return a value, whereas the ones that have two generic parameters like IRequestHandler<TRequest, TResponse> return a value. But your registrations will be driven by whether you use them or not.

    The ASP.NET Core integration piece

    You use Autofac, so don't worry about it. That package is designed to help you register your handlers for you if you use the ASP.NET Core DI container. It's certainly complex, because it does assembly scanning to find all the handlers in your code and register them for you. But again, no need for you to be worried since you use another container.