Autofac:有什么办法可以解决最里面的范围? [英] Autofac: any way to resolve the innermost scope?

查看:23
本文介绍了Autofac:有什么办法可以解决最里面的范围?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在过去几年中使用过 Ninject、Castle Windsor 和其他 IoC 容器后,我目前正在一个新的 ASP.NET MVC 项目中试用 Autofac.因此,虽然我大致了解 IoC 容器,但我对 Autofac 还是很陌生,我仍在寻找一些最佳实践.

I'm currently trying out Autofac in a new ASP.NET MVC project after having used Ninject, Castle Windsor and other IoC containers in the last years. So while I know about IoC containers in general, I'm fairly new to Autofac and I'm still looking for some best practices.

目前我正在尝试找出是否有办法解决最内层的嵌套作用域.

Currently I'm trying to find out if there is a way to resolve the innermost nested scope.

我有以下情况:注册为SingleInstance()的组件有一个方法创建嵌套的生命周期作用域,提供配置动作将一些组件配置为InstancePerLifetimeScope,在这个嵌套的作用域内解析注册的组件来做一些有用的东西,就像这样:

I have the following situation: a component that is registered as SingleInstance() has a method that creates a nested lifetime scope, providing a configuration action to configure some components as InstancePerLifetimeScope, and within this nested scope resolves the registered components to do something useful, like so:

ILifetimeScope currentScope = ???;

using (var scope = currentScope.BeginLifetimeScope(cb => {
  cb.RegisterType<X>().InstancePerLifetimeScope();
  // ...
}))
{
    var comp = scope.Resolve<X>();
    // ...
}

问题是我希望 currentScope 是最内层的生命周期范围,因为我知道 X 依赖于最内层范围内的组件.在最简单的情况下,例如当前请求生命周期范围.我当然可以使用 AutofacDependencyResolver.Current.RequestLifetimeScope 获得它,但我不想使用它,因为它不是很好的可测试的.此外,生命周期范围不一定是最里面的.

The issue is that I would like currentScope to be the innermost lifetime scope, because I know that X depends on components inside the innermost scope. In the simplest case that would be e.g. the current request lifetime scope. I can of course get it with AutofacDependencyResolver.Current.RequestLifetimeScope but I don't want to use that as it isn't really well testable. Also, that lifetime scope isn't necessarily the innermost.

那么,有没有办法找到给定的最内层生命周期范围,例如根容器还是不同的 ILifetimeScope?

So, is there a way to find the innermost lifetime scope given e.g. the root container or a different ILifetimeScope?

推荐答案

在 Autofac 中,最内层的作用域始终是容器.使用 AutofacDependencyResolver,它会是AutofacDependencyResolver.Current.ApplicationContainer

In Autofac, the innermost scope is always the container. Using the AutofacDependencyResolver, it'd be AutofacDependencyResolver.Current.ApplicationContainer

无法从嵌套作用域(如果您只有一个 ILifetimeScope)向后走"以到达容器.无论如何,我不一定确定您想这样做.

There is no way from a nested scope (if all you have is an ILifetimeScope) to "walk backward" to get to the container. I'm not necessarily sure you want to do that, anyway.

听起来您的 SingleInstance 组件正在执行某种服务定位,基本上是手动注册/解析某些组件.如果正在注册的类型集是固定的,我可能会建议(如果可能的话)对您的系统进行一些重新设计,因此 SingleInstance 组件不再注册为 SingleInstance,而是注册为 InstancePerDependency,然后将这些其他项目作为构造函数参数.

It sounds like your SingleInstance component is doing some sort of service location, basically, with manual registration/resolution of certain components. If the set of types being registered is fixed, I might recommend (if possible) some redesign of your system, so the SingleInstance component isn't registered as SingleInstance anymore and instead gets registered as InstancePerDependency, then have that take these other items in as constructor parameters.

代替...

// Consuming class like this...
public class BigComponent
{
  public void DoSomethingCool()
  {
    using(var scope = ...)
    {
      var c = scope.Resolve<SubComponent>();
      c.DoWork();
    }
  }
}

// ...and container registrations like this...
builder.RegisterType<BigComponent>().SingleInstance();

您可以尝试将其反转一下:

You might try inverting it a bit:

// Consuming class like this...
public class BigComponent
{
  private SubComponent _c;
  public BigComponent(SubComponent c)
  {
    _c = c;
  }
  public void DoSomethingCool()
  {
    _c.DoWork();
  }
}

// ...and container registrations like this...
builder.RegisterType<BigComponent>().InstancePerDependency();
builder.RegisterType<SubComponent>().InstancePerLifetimeScope();

这个想法是不必做即时注册和立即解决的事情.

The idea is to not have to do the on-the-fly registration-and-immediate-resolution thing.

如果您在进行服务定位时遇到困难,如果您需要绝对最内部的作用域,则需要使用 AutofacDependencyResolver.Current.ApplicationContainer,但请记住,您注册的任何对象的作用域都为 InstancePerHttpRequest 将无法解析,因此您可能会遇到麻烦.确实建议使用 AutofacDependencyResolver.Current.RequestLifetimeScope 代替.这将使您的方法:

If you're stuck doing service location, you'll need to use AutofacDependencyResolver.Current.ApplicationContainer if you need the absolute innermost scope, but keep in mind any objects you register scoped to InstancePerHttpRequest will not be resolvable if you do that, so you could get into trouble. It really is recommended to use the AutofacDependencyResolver.Current.RequestLifetimeScope instead. That would make your method:

var requestScope = AutofacDependencyResolver.Current.RequestLifetimeScope;
using (var scope = requestScope.BeginLifetimeScope(cb => {
  cb.RegisterType<X>().InstancePerLifetimeScope();
  // ...
}))
{
    var comp = scope.Resolve<X>();
    // ...
}

在测试环境中,AutofacDependencyResolver 允许您交换指示如何生成请求生命周期的提供程序.您可以像这样实现一个简单/存根:

In a testing environment, the AutofacDependencyResolver lets you swap in the provider that dictates how request lifetimes get generated. You can implement a simple/stub one like this:

public class TestLifetimeScopeProvider : ILifetimeScopeProvider
{
    readonly ILifetimeScope _container;
    private ILifetimeScope _lifetimeScope = null;

    public TestLifetimeScopeProvider(ILifetimeScope container)
    {
        if (container == null) throw new ArgumentNullException("container");
        _container = container;
    }

    public ILifetimeScope ApplicationContainer
    {
        get { return _container; }
    }

    public ILifetimeScope GetLifetimeScope()
    {
        if (_lifetimeScope == null)
        {
            _lifetimeScope = ApplicationContainer.BeginLifetimeScope("httpRequest")
        }
        return _lifetimeScope;
    }

    public void EndLifetimeScope()
    {
        if (_lifetimeScope != null)
            _lifetimeScope.Dispose();
    }
}

再说一次,只是用于单元测试的存根,而不是您在生产中使用过的东西.

Again, just a stub for unit testing, not something you'd ever use in production.

然后,当您在测试中连接 DependencyResolver 时,您将提供您的生命周期范围提供程序:

Then when you wire up the DependencyResolver in your test, you provide your lifetime scope provider:

var lsProvider = new TestLifetimeScopeProvider(container);
var resolver = new AutofacDependencyResolver(container, lsProvider);
DependencyResolver.SetResolver(resolver);

这让您可以在没有实际请求上下文的情况下使用 InstancePerHttpRequest 等内部单元测试.这也意味着您应该能够在注册/解析方法中使用请求生命周期范围,而不必依赖于应用程序容器.

This lets you use InstancePerHttpRequest and such inside unit tests without actually having a real request context. It also means you should be able to use the request lifetime scope in your registration/resolution method and not have to fall back on the application container.

这篇关于Autofac:有什么办法可以解决最里面的范围?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆