在 Global.asax 方法中获取在 Autofac 注册的组件的相同实例作为 InstancePerLifetimeScope 注入控制器?

Get same instance of a component registered with Autofac as InstancePerLifetimeScope in Global.asax methods as is injected into a controllers?

我有一种情况需要手动实例化 Application_BeginRequest 中的某些对象,这些对象依赖于我在 Autofac 中注册的某些相同组件。我想使用我用 InstancePerLifetimeScope 在 Autofac 注册的相同组件实例注入到我的 MVC 和 WebAPI 控制器中。我的 MVC 和 Web 配置 API 按预期工作,组件注册示例如下所示:

builder.Register(c => new MyDbContext()).AsSelf().InstancePerLifetimeScope();

现在我想在 class 中使用我在 Application_BeginRequest 中实例化的相同实例。我尝试了以下方法:

//Tried with MVC controllers
DependencyResolver.Current.GetService<MyDbContext>()));
AutofacDependencyResolver.Current.ApplicationContainer.Resolve<MyDbContext>()));
AutofacDependencyResolver.Current.RequestLifetimeScope.Resolve<MyDbContext>()));
//Tried with Web API controllers
GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(MyDbContext))

但是 none 给我我正在寻找的东西,即使是在请求生命周期的后期(即 BeginRequest 之后)。顺便说一句,我需要它来使用 Web API 配置,但我尝试了前 3 种方法只是为了看看我是否可以获得任何已解析的实例以匹配 Autofac 注入的内容。

我认为我对 Autofac 中的生命周期范围有一个很好的理解,我的假设是为我的应用程序控制器解析的实例在子范围内,即上述 4 个 none方法是指向的,但我不清楚上述每种方法在它们正在查看的范围以及它们如何决定方面试图做什么。更令人困惑的是 Autofac 自动为最终注入我的控制器的组件创建的生命周期范围以及它们的创建时间。

对以上几点的澄清将是一个很大的好处,但我的主要问题是如何让 Autofac 将 Global.asax 中已注册组件的相同实例交给我,它为 Web [=27] 解析=] 和 MVC 控制器?

嗯,我认为你应该尝试将 .InstancePerLifetimeScope() 更改为 .InstancePerRequest();

正如 Autofac 关于 InstancePerLifetimeScope 所说:

When you resolve the instance per lifetime scope component, you get a single instance per nested scope (e.g., per unit of work).

因此,当您执行 Resolve<MyDbContext>() 时,您可能在与控制器不同的生命周期范围内执行此操作(我猜这是因为您正在执行明确的解决方案);这就是为什么你得到一个不同的实例。

InstancePerRequest 改为:

Some application types naturally lend themselves to “request” type semantics, for example ASP.NET web forms and MVC applications. In these application types, it’s helpful to have the ability to have a sort of “singleton per request.”

你的请求的开始阶段已经在请求阶段,所以你应该在那里和你的控制器中得到相同的实例。

Instance per request builds on top of instance per matching lifetime scope by providing a well-known lifetime scope tag, a registration convenience method, and integration for common application types.

基于此,您可能也可以选择 .InstancePerMatchingLifetimeScope("myrequest"),但是您将不得不像这样 using(var scope1 = container.BeginLifetimeScope("myrequest")) 在任何地方手动实例化一个 Lifetime 范围;我觉得不太实用。

显然我想你不会在请求范围之外使用那些元素,否则你会得到一个异常。在这种情况下,您将被迫使用 MatchingLifetimeScope。

如果您需要更多详细信息,Autofac guide 非常清楚。

DependencyResolver.Current.GetService<MyDbContext>()));
AutofacDependencyResolver.Current.RequestLifetimeScope.Resolve<MyDbContext>()));

相同。他们获得当前请求生命周期范围。如果你在这里解决了一些实例,它会与控制器共享它。 (在这种情况下,每个请求的实例和每个生命周期的实例将相同。因为它们的生命周期相同)。

AutofacDependencyResolver.Current.ApplicationContainer.Resolve<MyDbContext>()));

这将从根容器中解决。这就是为什么它将为请求生命周期范围创建另一个实例。这不会被分享。这将存在于根容器中。 (如果你有每个请求实例,这将给出错误)。

我测试了第一个。它与 mvc 控制器共享 Application_BeginRequest 中解析的实例,但不与 Api 控制器共享实例。

然后我试图在 Application_BeginRequest 中获取 GlobalConfiguration.Configuration.DependencyResolver.GetRequestLifetimeScope() 它 returns null.

我认为,如果是 api 请求,Autofac 在 Application_BeginRequest 中还没有开始请求生命周期(可能与 .net 相关)。

因此,如果它是 api 请求并且我们无法达到 autofac 请求的生命周期。我不知道如何与 Application_BeginRequest.

中解决的 mvc 和 api 控制器共享此实例

可能Travis可以说清楚

如果你仔细阅读这个主题,你会注意到人们在大多数情况下提到 web 应用程序, InstancePerLifetimeScopeInstancePerRequest可以互换使用。他们没有提到的是这两个注册行为不同的例外情况。 Gerrod has an excellent article 关于 ASP.NET MVC/Web API 应用程序内部的生命周期范围以及它们如何工作,他详细阐述了这条最被忽略的信息。理解这种差异对于这种情况至关重要,他的文章消除了我对 InstancePerLifetimeScope 注册与 ASP.NET 申请的任何误解。这也让我意识到,因为我需要在 global.asax、MVC/Web API 控制器 之间共享已解析组件的实例,InstancePerLifetimeScope 不再适合此应用程序的注册方式 - 我现在需要使用 InstancePerRequest.

As per the docsInstancePerRequest 实际上在幕后使用 InstancePerMatchingLifetimeScope。我需要的是对根范围下标有 "AutofacWebRequest" 的子范围的引用。这是我的 MVC Web API 控制器解析它们的依赖关系的范围,因为它们都使用相同的标签。那么我如何获得对该特定范围的引用呢?这是我的解决方案,我很想知道是否有更好的方法。

首先,我需要更改我的注册

builder.Register(c => new MyDbContext()).AsSelf().InstancePerLifetimeScope();

builder.Register(c => new MyDbContext()).AsSelf().InstancePerRequest();

现在,在构建容器后,我有以下方法:

private void SetDependencyResolversForMvcAndWebApi(ILifetimeScope container)
{
    container.ChildLifetimeScopeBeginning += CaptureRequestLifetimeScope;
    DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
    GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}

之前唯一没有的部分是事件订阅。每次我的容器创建一个子作用域时,都会调用我的事件处理程序。事件处理程序如下所示:

private void CaptureRequestLifetimeScope(object sender, LifetimeScopeBeginningEventArgs args)
{
    if (args.LifetimeScope.Tag == MatchingScopeLifetimeTags.RequestLifetimeScopeTag)
    {
        //Get the ILifetimeScope created for components registered with InstancePerRequest
        var requestScope = args.LifetimeScope;

        //This is the same DbContext instance that will be injected into
        //my WebAPI and MVC controllers
        var context = requestScope.Resolve<MyDbContext>();
        //do the rest of my stuff
    }
}

我通过保留在 CaptureRequestLifetimeScope 事件处理程序中解析的已解析 DbContext 的引用并将其与注入到我的 Web API 和 MVC 控制器中的 DbContext 实例进行比较来测试它,它们确实指向同一个对象。