How to use MediatR with Autofac in ASP MVC 5?

The author provides an example on 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 tried to get it to 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 launch the web application, I get an error page informing me that the ServiceLocationProvider dependency is not registered. What am I doing wrong?

I suspect that the problem is that I register an instance of ServiceLocatorProvider after calling Build - in the author’s example, after that the Build method is called after Lazy<> . However, I do not know how to get around this.

+6
source share
3 answers

I had problems to correctly register the Mediator and ServiceLocatorProvider classes.
I pulled my hair a little, but finally I was able to get around it.

My mistake was to register the ServiceLocatorProvider in the Autofac root container, for example:

 var lazyContainer = new Lazy<IContainer>(() => builder.Build()); builder.Register(x => new ServiceLocatorProvider(() => new AutofacServiceLoator(lazyContainer.Value))); DependencyResolver.SetCurrent(new AutofacDependencyResolver(lazyContainer.Value); 

At run time, Autofac threw an exception because one of my Request depended on my EF DbContext , which I configured to use in an HTTP request.

The trick is to register a ServiceLocatorProvider in relation to the current ILifetimeScope HTTP request.
Fortunately, the Autofac error message is self-evident:

 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 is because the AutofacServiceLocator been loaded by the root container.
The container does not know about the internal DbContext associated with the current HTTP request when requesting DbContext permission.

Using JustDecompile, I sax that the only class that implements the ILifetimeScopeProvider interface was AutofacDependencyResolver , and you can access the current instance with the static AutofacDependencyResolver.Current property.

You can access the current ILifetimeScope using AutofacDependencyResolver.Current.RequestLifetimeScope as described in the error message, so at the end the registration looks like this:

 builder .Register(x => new ServiceLocatorProvider(() => new AutofacServiceLocator(AutofacDependencyResolver.Current.RequestLifetimeScope))) .InstancePerHttpRequest(); 

The InstancePerHttpRequest() is optional, but since ILifetimeScope will be the same throughout the entire HTTP request, this will prevent Autofac from creating n instances of AutofacServiceLocator .

I hope this was clear, feel free to make some changes if you think it is necessary, because it is difficult for me to explain this clearly.

+3
source

I am using Webapi 2 + Autofac + OWIN and can make it work. 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 there was no need to identify every Handler or CommandHandler request. Since I also lost a lot of time to put it on the list, I hope this helps others with the same problem. Past answers were helpful to get to this.

UPDATE:

Ok, I'm just reorganizing the code to remove all lazy bindings, making it a lot easier. The changes are listed below:

Instead:

  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); 
+2
source

You cannot invoke Builder.Build before completing type registration.

in your example, you call Builder.Build before calling builder.RegisterInstance, which explains why it cannot infer a type at runtime.

I ran into this, having run into the same problem today, but this is a work in progress, as it still does not resolve my implementations ...

 builder.RegisterSource(new ContravariantRegistrationSource()); builder.RegisterAssemblyTypes(typeof(IMediator).Assembly).AsImplementedInterfaces(); builder.RegisterAssemblyTypes(typeof(HomePageThumbnail).Assembly).AsImplementedInterfaces(); var lifetimeScope = new Lazy<ILifetimeScope>(() => builder.Build()); var lazy = new Lazy<IServiceLocator>(() => new AutofacServiceLocator(lifetimeScope.Value)); var serviceLocatorProvider = new ServiceLocatorProvider(() => lazy.Value); builder.RegisterInstance(serviceLocatorProvider); DependencyResolver.SetResolver(new AutofacDependencyResolver(lifetimeScope.Value)); app.UseAutofacMiddleware(lifetimeScope.Value); app.UseAutofacMvc(); 

I create a container in a separate Lazy<T> and passing it.

So far, this assembly and site loading cannot deduce which handler to use for the request in my controller action ...

 var response = _mediator.Send(new RecentActivityThumbnailsQuery()); 

which raises an exception ...

  An exception of type 'Microsoft.Practices.ServiceLocation.ActivationException' occurred in Microsoft.Practices.ServiceLocation.dll but was not handled in user code Additional information: Activation error occurred while trying to get instance of type IRequestHandler`2, key "" 

This uses the same agreement-based registration provided in the Autofac sample project included in MediatR, and I also tried to register it explicitly ....

 builder.RegisterType<RecentActivityThumbnailsHandler>().As<IRequestHandler<RecentActivityThumbnailsQuery, RecentActivityThumbnailsResults>>(); 

I will update when I get his work. Please do the same if you find out before I do this.

0
source

Source: https://habr.com/ru/post/979461/


All Articles