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?
在 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
此类内部单元测试,而无需实际拥有真正的请求上下文。这还意味着您应该能够在注册/解析方法中使用请求生命周期范围,而不必依赖于应用程序容器。
归档时间: |
|
查看次数: |
7237 次 |
最近记录: |