How do I use MediatR with Autofac in ASP MVC 5? - c#

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);

You cannot call Builder.Build before you finish registering types.
in your example you are calling Builder.Build before you call builder.RegisterInstance which explains why it cannot infer the type at runtime.
I came up with this after hitting the same issue today, but it is work in progress as it doesnt resolve my implementations yet...
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 am creating the container in a separate Lazy<T> and passing that around.
While this builds and the site loads, it cannot infer which handler to use for the request in my controller action...
var response = _mediator.Send(new RecentActivityThumbnailsQuery());
which raises the 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 is using the same convention based registration provided in the Autofac examples project included in MediatR, and i have also tried registering it explicitly....
builder.RegisterType<RecentActivityThumbnailsHandler>().As<IRequestHandler<RecentActivityThumbnailsQuery, RecentActivityThumbnailsResults>>();
I will update when i get it working. Please do the same if you figure it out before i do.

Related

How do I configure Autofac so my DbContext is created each time new?

I am new to Autofac. In my program.cs file I have my registrations:
var builder = new ContainerBuilder();
builder.RegisterLogger();
builder.RegisterType<GetUserFactory>().As<IGetUser>();
builder.RegisterType<MyContext>().InstancePerLifetimeScope();
builder.RegisterType<LoginForm>();
builder.RegisterType<RegisterForm>();
builder.RegisterType<MainFormView>().As<IMainFormView>().InstancePerLifetimeScope();
builder.RegisterType<MainFormPresenter>();
builder.RegisterType<TestForm>();
builder.RegisterType<PeopleReport>();
Container = builder.Build();
When I open my MainFormView it works fine the first time. But any attempt to open it a second time or any subsequent attempt states my MyContext is disposed.
using (var mainFormView = Program.Container.Resolve<IMainFormView>())
{
mainFormView.SetTag(Program.Container.Resolve<MainFormPresenter>());
var form = (MainFormView)mainFormView;
form.ShowDialog();
}
My question is how should I configure Autofac so my MyContext is new-ed up for each MainFormView request?
UPDATE:
After looking more closely at my error message I see it is my MainFormView that is being disposed that is the reason for the error message not the MyContext.
I see you've got:
builder.RegisterType<MyContext>()
.InstancePerLifetimeScope();
^^^^^^^^^^^^^
Database contexts should generally not be declared to have a lifetime scope. See also and also

what the difference between RegisterInstance and RegisterType in DI Autofac

I am new in Autofac and i am trying to understand the difference between RegisterInstance and RegisterType in a web api 2 (.Net framework).
Here in his doc have a simple example
var builder = new ContainerBuilder();
// Register individual components
builder.RegisterInstance(new TaskRepository())
.As<ITaskRepository>();
builder.RegisterType<TaskController>();
builder.Register(c => new LogManager(DateTime.Now))
.As<ILogger>();
// Scan an assembly for components
builder.RegisterAssemblyTypes(myAssembly)
.Where(t => t.Name.EndsWith("Repository"))
.AsImplementedInterfaces();
var container = builder.Build();
Can someone explain it?
RegisterInstance registers a single instance that will be then used as singleton.
RegisterType leaves the creation and lifetime to the container. The default is usually per-request creation.

ComponentNotRegisteredException even with registered instance

I have an interface IMyInterface and I am registering a Moq object for unit testing like this
var myMockObject = new Mock<IMyInterface>();
myMockObject.Setup(a => a.MyMethod(It.IsAny<string>()))
.Returns(new MyResult()
{
Props1 = "Testing123",
}).Callback(() =>
{
});
builder.RegisterInstance(myMockObject).As<IMyInterface>();
var test = container.Resolve<IMyInterface>();
But I am always getting the following error:
Autofac.Core.Registration.ComponentNotRegisteredException: 'The
requested service 'IMyInterface' has not been registered. To avoid
this exception, either register a component to provide the service,
check for service registration using IsRegistered(), or use the
ResolveOptional() method to resolve an optional dependency.'
One thing to note is that I am accessing the AutoFac builder inside a static class. I have made sure that the autofac builder instance is only created once.
UPDATE it looks like I have to call builder.Build() after registering the instance. I guess all instances and types have to be registered and after that you call Build(). So it's not really possible to register more instances after that.
You are not building your Autofac container after registering.
builder.RegisterInstance(myMockObject).As<IMyInterface>();
// Create the DI container
var container = builder.Build();
var test = container.Resolve<IMyInterface>();

Configuring ASP .NET MVC Core controller instantiation without using ConfigureServices

In the good old days of OWIN, prior to aspnet core, one could configure ASP.NET WebApi by passing in a HttpConfiguration object. In there one could, among other things configure the ControllerActivator etc... if you would like to.... e.g.
appBuilder.UseWebApi(webApiConfig)
Is there a similar way one can do this in ASP.NET MVC Core, avoiding the using the WebHostBuilder (either directly or via the StartUp convention)?
e.g. without using something like:
var host = new WebHostBuilder()
.ConfigureServices(serviceCollection =>
serviceCollection.AddSingleton<IControllerActivator>(new
ControllerActivator()))
(The only overloads one can use on .UseMVC takes an IRouteBuilder ... Or is there a way to get to the ServiceCollection to manipulate the ControllerActivator?)
EDIT:
There is an option to create a IServiceProvider using the ServiceCollection class. The IServiceProvider can then be passed as an argument to the ApplicationBuilder like so:
var serviceCollection = new ServiceCollection();
serviceCollection.AddMvcCore();
var serviceProvider = serviceCollection
.AddSingleton<IControllerActivator>(new ControllerActivator())
.AddLogging()
.BuildServiceProvider();
ApplicationBuilder builder = new ApplicationBuilder(serviceProvider);
builder.UseMvc();
return builder.Build();
However, this leaves me with as exception:
System.InvalidOperationException: Unable to resolve service for type 'Microsoft.Extensions.Options.IOptions`1[Microsoft.AspNetCore.Mvc.MvcOptions]' while attempting to activate 'Microsoft.AspNetCore.Mvc.ModelBinding.ModelBinderFactory'
Investigating further....
So... there are a few required services that need to be present to at least get the pipeline running without exceptions. My primary goal was to add a custom IControllerActivator. This is what I ended up with (after peeking into WebHostBuilder source code):
var listener = new DiagnosticListener("Microsoft.AspNetCore");
var serviceCollection = new ServiceCollection();
serviceCollection.AddMvcCore();
var serviceProvider = serviceCollection
.AddSingleton<IControllerActivator>(new ControllerActivator())
.AddLogging()
.AddSingleton<DiagnosticSource>(listener)
.AddOptions()
.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
.BuildServiceProvider();
...
ApplicationBuilder builder = new ApplicationBuilder(serviceProvider);
builder.UseMvc();
return builder.Build();
...
Hope it helps someone..

Accessing Variables Created In Application_Start ASP.NET MVC 3

I have the following code running in my Application_Start method:
var builder = new ContainerBuilder();
var store = new DocumentStore { Url = "http://localhost:8081" };
store.Initialize();
builder.RegisterInstance(store);
var container = builder.Build();
I am using AutoFac to store the instance of my RavenDB DocumentStore. Now I know this only runs once when the application is started however how would I be able to access the container variable so that I can retrieve the DocumentStore that in stored in there from anywhere in my application.
The idea of DI is that you configure your container in the Application_Start and you wire all the necessary dependencies into your objects so that you never need to access the container in other parts of your code. So to answer your question: simply have the parts of your application that need to access the DocumentStore take it as constructor argument and then configure AutoFac to inject it.
Having other parts of your code depending on the container is a bad practice as they become tightly coupled to it.
Ok! As Darin pointed out, it's not a good practice but if you want to,
you could do
var container = builder.Build();
Application["container"] = container;
and access it by
var container = Application["container"] as Container; // assuming Container is the type

Categories

Resources