Autofac:有什么办法可以解决最深层的范围?

And*_*ker 5 asp.net-mvc ninject autofac asp.net-mvc-3

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

当前,我正在尝试找出是否有一种方法可以解决最内层的嵌套作用域。

我遇到以下情况:注册为SingleInstance()的组件具有创建嵌套生存期范围的方法,该方法提供配置操作以将某些组件配置为InstancePerLifetimeScope,并在此嵌套范围内解析已注册的组件以执行有用的操作,像这样:

ILifetimeScope currentScope = ???;

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

问题是我希望currentScope成为最内部的生存期范围,因为我知道X取决于最内部的范围内的组件。在最简单的情况下,例如当前请求生存期范围。我当然可以通过AutofacDependencyResolver.Current.RequestLifetimeScope来获取它,但是我不想使用它,因为它并不是很好的测试工具。而且,该生存期范围不一定是最内层的。

因此,有没有办法找到给定的最内部生命周期范围,例如根容器或其他ILifetimeScope?

Tra*_*lig 6

在 Autofac 中,最里面的作用域始终是容器。使用 AutofacDependencyResolver,它是 AutofacDependencyResolver.Current.ApplicationContainer

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

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

代替...

// 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();
Run Code Online (Sandbox Code Playgroud)

你可以尝试稍微颠倒一下:

// 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();
Run Code Online (Sandbox Code Playgroud)

这个想法是不必进行即时注册和立即解决的事情。

如果您陷入服务定位困境,则需要使用AutofacDependencyResolver.Current.ApplicationContainer绝对最内层作用域,但请记住,InstancePerHttpRequest如果您这样做,您注册作用域的任何对象都将无法解析,因此您可能会遇到麻烦。确实建议使用AutofacDependencyResolver.Current.RequestLifetimeScope来代替。这将使你的方法:

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

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

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

var lsProvider = new TestLifetimeScopeProvider(container);
var resolver = new AutofacDependencyResolver(container, lsProvider);
DependencyResolver.SetResolver(resolver);
Run Code Online (Sandbox Code Playgroud)

这使您可以使用InstancePerHttpRequest此类内部单元测试,而无需实际拥有真正的请求上下文。这还意味着您应该能够在注册/解析方法中使用请求生命周期范围,而不必依赖于应用程序容器。