如何从autofac父范围解析而不将其多次传递?

时间:2018-06-26 16:28:55

标签: c# .net autofac

我有一个在某个时候创建​​生命周期范围的应用程序,

    public class Main
    {
        public void Main()
        {
            using (ILifetimeScope scope = AutofacContainer.Container.BeginLifetimeScope())
            {
                scope.Resolve<SomeClass>();
            }
        }
    }

SomeClass内,我有一个逻辑,该逻辑然后调用许多不同的类,依此类推。 然后,在调用堆栈中大约有10种方法,我需要使用主作用域来做到这一点:

public class ActivatorFactory : IActivatorFactory
{
    public T Create<T>(Type instance)
    {
        using (ILifetimeScope scope = AutofacContainer.Container.BeginLifetimeScope())
        {
            return (T)scope.Resolve(instance);
        }
    }
}

问题在于,现在我创建了一个新作用域,该作用域仅用于解析运行时类型。我希望能够使用主作用域来解决此类型。在不通过10种不同的方法/功能将主范围传递给该工厂类的情况下,该怎么办? 我想到的唯一“ hacky”解决方案是在ActivatorFactory类上仅具有一个静态属性,并在Main类中设置范围,如下所示:

    public class Main
    {
        public void Main()
        {
            using (ILifetimeScope scope = AutofacContainer.Container.BeginLifetimeScope())
            {
                ActivatorFactory.Scope = scope;
                scope.Resolve<SomeClass>();
            }
        }
    }

是否有更干净的解决方案可以在应用程序的另一部分中使用主范围?

2 个答案:

答案 0 :(得分:1)

在每个生命周期范围内,我都需要一个CancellationTokenSource实例,其中子级与父级链接。如果root范围的CancellationTokenSource被取消,则所有子生存期范围的CancellationToken被取消。为此,我创建了:

private sealed class ParentLifetimeScopeAccessor
{
    private readonly ILifetimeScope _lifetimeScope;

    public ParentLifetimeScopeAccessor(ILifetimeScope lifetimeScope)
    {
        _lifetimeScope = lifetimeScope;
        _lifetimeScope.ChildLifetimeScopeBeginning += OnChildLifetimeScopeBeginning;
    }

    public ILifetimeScope ParentLifetimeScope { get; private set; }

    private void OnChildLifetimeScopeBeginning(object sender, LifetimeScopeBeginningEventArgs e) =>
        e.LifetimeScope.Resolve<ParentLifetimeScopeAccessor>().ParentLifetimeScope = _lifetimeScope;
}

注册后,您现在可以访问父母的范围:

builder.RegisterType<ParentLifetimeScopeAccessor>().InstancePerLifetimeScope();

使用父生命周期作用域访问器,可以创建链接的CancellationTokenSource实例:

private static CancellationTokenSource CancellationTokenSourceFactory(IComponentContext context)
{
    var scopeAccessor = context.Resolve<ParentLifetimeScopeAccessor>();
    var parentScope = scopeAccessor.ParentLifetimeScope;
    return null == parentScope
        ? new CancellationTokenSource()
        : CancellationTokenSource.CreateLinkedTokenSource(parentScope.Resolve<CancellationTokenSource>().Token);
}

CancellationToken解析器:

private static CancellationToken CancellationTokenResolver(IComponentContext context) =>
context.Resolve<CancellationTokenSource>().Token;

两个注册:

builder.Register(CancellationTokenSourceFactory).AsSelf().InstancePerLifetimeScope();
builder.Register(CancellationTokenResolver).AsSelf().InstancePerDependency();

答案 1 :(得分:0)

如果您的应用未使用ActivatorFactory (如果您使用的是控制反转,则不应该使用),则将其删除,然后考虑一下您要使用的是什么尝试测试。

  • 您是否要测试您通常可以从Autofac那里解决问题? Autofac拥有大量的单元测试以及数百万成功的用户。测试框架没有任何价值。
  • 您是否要测试是否已注册所有需要注册的东西?出于以下两个原因,这也没有太多价值:首先,您要实现这一目标在运行时很快,可以在那些测试中看到它;其次,在一个大型的,分离的系统中,这些测试很快就会过时。这是一个维护麻烦。
  • 您是否要测试是否可以根据您的注册组成特定的对象图?,我可能会购买这个。参见下文。

让我们说这是最后一件事-您有一个非常复杂且麻烦的对象图,您想确保可以创建它,因为人们一直在破坏它。我可以看到。

将注册分离到一个Autofac模块中。使用Autofac模块进行测试。

public class MyRegistrations : Autofac.Module
{
  protected override void Load(ContainerBuilder builder)
  {
    builder.RegisterType<Thing>();
    // and all your other registrations.
  }
}

然后在单元测试中

var builder = new ContainerBuilder();
builder.RegisterModule<MyRegistrations>();
var container = builder.Build();
var thing = container.Resolve<Thing>();
// Assert on the resolved thing.

您可以使用相同的模块将您的注册封装在应用程序中,然后您实际上将在测试复杂的注册,但是没有未使用的工厂。

警告:测试一些复杂的注册与测试所有注册之间的滑坡。就像我说的那样,您真的不想测试您拥有的每个注册。我跌倒了。这是一场维护噩梦。向模块/应用添加注册,添加测试。嗯,我们重构了,现在注册都不同了。啊。对行为的测试要比对特征的测试少(不是“我要它做什么”,而是“现在要做什么”)。疼痛。痛苦。

如果您正在在应用中使用ActivatorFactory ,例如,将其作为服务位置,而不是使用已经存在的CommonServiceLocator之类的更标准的东西为您和Autofac already directly integrates ...为您执行此操作,然后仅使用真实容器对ActivatorFactory进行测试,但使用一些任意测试注册,而不是真实应用程序中的整个测试集。 ActivatorFactory的功能与其中注册的内容没有任何关系。

是的,如果您使用的是ActivatorFactory,并且需要保留它,则必须在应用启动时将其交给ILifetimeScope。这就是服务定位器的工作方式。在查看如何与ASP.NET,WCF等应用程序集成时,您会在Autofac docs中看到所有这些信息。