The author provides an example of how to use MediatR in a console application using Autofac:
var builder = new ContainerBuilder();
builder.RegisterSource(new ContravariantRegistrationSource());
builder.RegisterAssemblyTypes(typeof (IMediator).Assembly).AsImplementedInterfaces();
builder.RegisterAssemblyTypes(typeof (Ping).Assembly).AsImplementedInterfaces();
builder.RegisterInstance(Console.Out).As<TextWriter>();
var lazy = new Lazy<IServiceLocator>(() => new AutofacServiceLocator(builder.Build()));
var serviceLocatorProvider = new ServiceLocatorProvider(() => lazy.Value);
builder.RegisterInstance(serviceLocatorProvider);
I took this example and attempted to make it work with ASP MVC 5 and the Autofac.Mvc5 package:
var builder = new ContainerBuilder();
builder.RegisterSource(new ContravariantRegistrationSource());
builder.RegisterAssemblyTypes(typeof(IMediator).Assembly).AsImplementedInterfaces();
builder.RegisterAssemblyTypes(typeof(AddPostCommand).Assembly).AsImplementedInterfaces();
builder.RegisterControllers(typeof(HomeController).Assembly);
var container = builder.Build();
var lazy = new Lazy<IServiceLocator>(() => new AutofacServiceLocator(container));
var serviceLocatorProvider = new ServiceLocatorProvider(() => lazy.Value);
builder.RegisterInstance(serviceLocatorProvider);
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
When I run the web application, I get an error page telling me that the ServiceLocationProvider
dependency has not been registered. What am I doing wrong?
I suspect that the problem is due to the fact that I am registering the ServiceLocatorProvider
instance after calling Build
- in the author's example, the Build
method is invoked afterwards thanks to Lazy<>
. I do not know how to work around this, though.
I had issues to properly register both the Mediator
and ServiceLocatorProvider
classes.
I pulled my hair for a while, but finally managed to get around it.
My mistake was to register ServiceLocatorProvider
against the root Autofac container, like this:
var lazyContainer = new Lazy<IContainer>(() => builder.Build());
builder.Register(x => new ServiceLocatorProvider(() => new AutofacServiceLoator(lazyContainer.Value)));
DependencyResolver.SetCurrent(new AutofacDependencyResolver(lazyContainer.Value);
At runtime, Autofac was throwing an exception because one of my Request
had a dependency on my EF DbContext
that I configured to be scoped by HTTP request.
The trick is to register the ServiceLocatorProvider
against the current HTTP request ILifetimeScope
.
Thankfully, the error message from Autofac is self explanatory:
No scope with a Tag matching 'AutofacWebRequest' is visible from the scope in which the instance
was requested. This generally indicates that a component registered as per-HTTP request is being
requested by a SingleInstance() component (or a similar scenario.) Under the web integration
always request dependencies from the DependencyResolver.Current or ILifetimeScopeProvider.RequestLifetime,
never from the container itself.
This happens because the AutofacServiceLocator
was fed with the root container.
The container, when asking to resolve the DbContext
, has no knowledge of an inner - associated with to the current HTTP request - scope.
Using JustDecompile, I sax that the only class implementing the ILifetimeScopeProvider
interface was AutofacDependencyResolver
, and you can access the current instance with the static property AutofacDependencyResolver.Current
.
You can access the current ILifetimeScope
by using AutofacDependencyResolver.Current.RequestLifetimeScope
as explained in the error message, so in the end the registration looks like:
builder
.Register(x => new ServiceLocatorProvider(() => new AutofacServiceLocator(AutofacDependencyResolver.Current.RequestLifetimeScope)))
.InstancePerHttpRequest();
The InstancePerHttpRequest()
part is optional but since the ILifetimeScope
will be the same during the whole HTTP request, this prevents Autofac from creating n instances of AutofacServiceLocator
.
Hope this was clear, don't hesitate to make some edits if you feel like it's necessary, because I have a hard time explaining this clearly.
I am using Webapi 2 + Autofac + OWIN and manage to get it working. Here is my code:
Here is my autofac Constructor
//Constructor
var builder = new ContainerBuilder();
builder.RegisterSource(new ContravariantRegistrationSource());
builder.RegisterAssemblyTypes(typeof(IMediator).Assembly).AsImplementedInterfaces();
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsImplementedInterfaces();
// Register Web API controller in executing assembly.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).InstancePerRequest();
var lazyContainer = new Lazy<IContainer>(() => builder.Build());
var serviceLocatorProvider = new ServiceLocatorProvider(() => new AutofacServiceLocator(lazyContainer.Value));
builder.RegisterInstance(serviceLocatorProvider);
config.DependencyResolver = new AutofacWebApiDependencyResolver(lazyContainer.Value);
// This should be the first middleware added to the IAppBuilder.
app.UseAutofacMiddleware(lazyContainer.Value);
// Make sure the Autofac lifetime scope is passed to Web API.
app.UseAutofacWebApi(config);
Here are my namespaces:
using System;
using System.Reflection;
using System.Web.Http;
using Autofac;
using CommonServiceLocator.AutofacAdapter.Unofficial;
using Autofac.Features.Variance;
using Autofac.Integration.WebApi;
using MediatR;
using Microsoft.Practices.ServiceLocation;
using Owin;
Everything worked fine and did not had to explict register every requestHandler or CommandHandler. Since I also lost a LOT of time to put it togueter I do hope that it will help others having the same issue. Past answers were helpfull to get to this one.
UPDATE:
Well, I just refactor de code to remove all the lazy binding making it much simpler. Bellow are the changes:
Instead of:
var lazyContainer = new Lazy<IContainer>(() => builder.Build());
var serviceLocatorProvider = new ServiceLocatorProvider(() => new AutofacServiceLocator(lazyContainer.Value));
builder.RegisterInstance(serviceLocatorProvider);
Just use :
builder.RegisterType<AutofacServiceLocator>().AsImplementedInterfaces();
var container = builder.Build();
//ServiceLocator.SetLocatorProvider(serviceLocatorProvider);
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With