6

作者提供了一个如何在使用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依赖项尚未注册。我究竟做错了什么?

怀疑问题是由于我在调用ServiceLocatorProvider注册了实例- 在作者的示例中,该方法随后被调用,这要归功于. 不过,我不知道如何解决这个问题。BuildBuildLazy<>

4

3 回答 3

3

我在正确注册MediatorServiceLocatorProvider类时遇到问题。
我拉了一会儿头发,但最终设法绕过它。

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

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 请求限定范围。

诀窍是ServiceLocatorProvider针对当前的 HTTP 请求进行注册ILifetimeScope
值得庆幸的是,来自 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

您可以按照错误消息中的说明访问当前,因此最终注册如下ILifetimeScope所示:AutofacDependencyResolver.Current.RequestLifetimeScope

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

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

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

于 2014-12-24T15:14:09.350 回答
2

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

这是我的 autofac 构造函数

        //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。由于我也浪费了很多时间来讨论它,我希望它能帮助其他有同样问题的人。过去的答案有助于解决这个问题。

更新:

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

代替:

        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);
于 2015-01-21T12:43:25.140 回答
0

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

在您的示例中,您在调用 builder.RegisterInstance 之前调用了 Builder.Build,这解释了为什么它无法在运行时推断类型。

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

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

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

于 2014-12-19T19:17:17.180 回答