如何在 ASP MVC 5 中将 MediatR 与 Autofac 一起使用

本文关键字:Autofac 一起 MediatR 中将 ASP MVC | 更新日期: 2023-09-27 18:33:56

作者提供了一个如何使用Autofac在控制台应用程序中使用MediatR的示例:

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

我举了这个例子,并试图让它与ASP MVC 5和Autofac.Mvc5包一起工作:

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

当我运行 Web 应用程序时,我收到一个错误页面,告诉我尚未注册ServiceLocationProvider依赖项。我做错了什么?

怀疑问题是由于我在调用Build注册了ServiceLocatorProvider实例 - 在作者的示例中,由于 Lazy<> 而事后调用了 Build 方法。不过,我不知道如何解决这个问题。

如何在 ASP MVC 5 中将 MediatR 与 Autofac 一起使用

我在正确注册Mediator类和ServiceLocatorProvider类时遇到了问题。
我扯了一会儿头发,但终于设法绕过了它。

我的错误是针对根 Autofac 容器注册ServiceLocatorProvider,如下所示:

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

在运行时,Autofac 引发异常,因为我的一个Request依赖于我的 EF DbContext,我将其配置为由 HTTP 请求限定范围。

诀窍是针对当前 HTTP 请求ILifetimeScope注册ServiceLocatorProvider
值得庆幸的是,来自Autofac的错误消息是不言自明的:

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.

发生这种情况是因为AutofacServiceLocator是用根容器馈送的。
容器在请求解析DbContext时,不知道与当前HTTP请求关联的内部范围。

使用 JustDecompile,我认为实现ILifetimeScopeProvider接口的唯一类是 AutofacDependencyResolver ,您可以使用静态属性 AutofacDependencyResolver.Current 访问当前实例。

您可以使用错误消息中所述AutofacDependencyResolver.Current.RequestLifetimeScope访问当前ILifetimeScope,因此最终注册如下所示:

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

InstancePerHttpRequest()部分是可选的,但由于在整个HTTP请求期间ILifetimeScope相同,因此这会阻止Autofac创建AutofacServiceLocator的n个实例。

希望这很清楚,如果您觉得有必要,请不要犹豫进行一些编辑,因为我很难清楚地解释这一点。

我正在使用 Webapi 2 + Autofac + OWIN 并设法让它工作。这是我的代码:

这是我的自动构造函数

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

这是我的命名空间:

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;

一切正常,不必显式注册每个requestHandler或CommandHandler。由于我也浪费了很多时间来解决这个问题,我确实希望它能帮助其他有同样问题的其他人。过去的答案有助于解决这个问题。

更新:

好吧,我只是重构 de 代码以删除所有惰性绑定,使其更简单。波纹管是变化:

而不是:

        var lazyContainer = new Lazy<IContainer>(() => builder.Build());
        var serviceLocatorProvider = new ServiceLocatorProvider(() => new AutofacServiceLocator(lazyContainer.Value));
        builder.RegisterInstance(serviceLocatorProvider);

只需使用 :

        builder.RegisterType<AutofacServiceLocator>().AsImplementedInterfaces();
        var container = builder.Build();
        //ServiceLocator.SetLocatorProvider(serviceLocatorProvider);            
        config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

在完成类型注册之前,不能调用 Builder.Build。

在您的示例中,您在调用构建器之前调用 Builder.Build。RegisterInstance解释了为什么它不能在运行时推断类型。

今天遇到同样的问题后,我想出了这个,但它正在进行中,因为它还没有解决我的实现......

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

我正在一个单独的Lazy<T>中创建容器并将其传递。

当此生成并加载站点时,它无法推断哪个处理程序用于我的控制器操作中的请求...

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

这引发了异常...

    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 ""

这是使用MediatR中包含的Autofac示例项目中提供的基于约定的相同注册,我也尝试显式注册它。

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

当我让它工作时,我会更新。 如果你在我之前弄清楚,请做同样的事情。