Autofac生命周期和匹配生命周期范围内的默认提供程序

6

我有一个使用Autofac进行依赖注入的ASP.NET MVC Web应用程序。偶尔,这个Web应用程序会启动一个线程,以便在请求线程之外执行一些工作。当这个后台线程启动时,它会从根容器建立一个新的Autofac生命周期范围,并运行一些操作。

public IAsyncResult Run<T>(Action<T> action)
{
    var NewTask = System.Threading.Tasks.Task.Factory.StartNew(() =>
    {
        using (var Scope = Runtime.Container.BeginLifetimeScope())
        {
            var Input = Scope.Resolve<T>();
            action(Input);
        }
    });

    return NewTask;
}

我的一个依赖项在Autofac中注册了两个不同的实现:一个适用于http请求生命周期,另一个适用于所有其他生命周期。我尝试按以下方式进行注册:
builder
.Register(c => new Foo())
.As<IFoo>()
.InstancePerLifetimeScope();

builder
.Register(c => new FooForHttp(HttpContext.Current))
.As<IFoo>()
.InstancePerMatchingLifetimeScope(WebLifetime.Request);

Autofac会为http请求选择FooForHttp(正如预期的那样)。然而,当我的后台线程启动时,任何尝试解析IFoo的操作都会导致异常:

在请求实例的作用域中,没有与“httpRequest”匹配的标记范围可见。这通常表示正在请求一个按HTTP请求注册的组件,但该组件是由SingleInstance()组件(或类似情况)请求的。在Web集成下,请始终从DependencyResolver.Current或ILifetimeScopeProvider.RequestLifetime请求依赖项,而不是从容器本身请求。

我知道Autofac总是使用最后注册的提供程序作为特定组件的默认提供程序。我在这里做了一个假设,即它将使用最后注册的合适的提供程序。
我是否遗漏了什么,或者有没有更好的方法基于当前生命周期范围的标记选择提供程序?
2个回答

7

正常注册web Foo,但不要注册其他Foo。在创建异步任务的生命周期范围时,使用BeginLifetimeScope()的重载版本,并在ContainerBuilder上执行操作。在此操作中注册后台Foo(b => b.Register()),这将覆盖Web中的Foo。(抱歉我的键盘很小 :))


谢谢 - 我错过了BeginLifetimeScope的重载,而那正是我所需要的! - Michael Petito
FYI:Autofac.Integration.Web.ContainerProvider 有一个类似的构造函数重载,它接受相同的操作。我决定从那一端提供覆盖,因为覆盖是特定于 Web 请求的。 - Michael Petito
如果您必须针对范围进行自定义注册,那么标签有什么意义呢?据我所知,这个标签可以被省略。我真的希望能够使用此标签功能来选择两个具有不同标签的注册选项,但似乎最后一个标记的注册覆盖了之前的注册,这让我无法理解。那么标记的作用是什么呢? - nietras
@harrydev 我昨天又遇到了这个问题,虽然每个范围的自定义注册可以工作,但是这里提出了另一个潜在的解决方案:https://dev59.com/xW7Xa4cB1Zd3GeqPlAJa#14722537,它使用新的组件生命周期“MatchingScopeOrRootLifetime”扩展了Autofac。如果您只需要在当前生命周期范围不匹配(或不存在)时返回默认的单例实例,则此方法也可能对您有所帮助。 - Michael Petito
@MichaelPetito 谢谢。我已经看到了这个,但很遗憾它并没有真正帮助我,因为我实际上有超过两种不同的情况需要选择,所以我希望您可以通过简单地使用不同的标签(例如“A”,“B”,“C”)来注册它们,然后创建一个带有“ A”标签的标记生命周期范围,然后将为此注册的组件用于所有作为PerMatchingLifetimeScope注册的组件... - nietras

1
这也可以通过使用带标记的生命周期范围来解决。 将您的第一个Foo注册为您标记范围的实例:
builder.RegisterType<Foo>().As<IFoo>.InstancePerMatchingLifetimeScope("YourScopeTag");

使用与注册依赖项相同的标签创建作用域:

  using (var Scope = Runtime.Container.BeginLifetimeScope("YourScopeTag"))
        {
            var Input = Scope.Resolve<T>();
            action(Input);
        }

尚未测试,但应该可以正常运行。

http://docs.autofac.org/en/latest/lifetime/instance-scope.html#instance-per-matching-lifetime-scope


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接