Autofac多次注册组件

问题描述 投票:2回答:1

[In a previous question about got I visualize the graph of my dependencies我为现在用来可视化依赖关系图的代码奠定了基础,因为它由Autofac解析。

运行代码,我得到一棵树,结果如下所示。

Usd.EA.Bogfoering.WebApi.Controllers.BogfoerController (3851,7 ms. / 0,0 ms.) Depth: 0
   Usd.EA.Bogfoering.WebApi.Controllers.BogfoerController (3851,7 ms. / 0,4 ms.) Depth: 1
      Usd.Utilities.WebApi.Controllers.UnikOwinContext (0,1 ms. / 0,0 ms.) Depth: 2
         Usd.Utilities.WebApi.Controllers.UnikOwinContext (0,1 ms. / 0,0 ms.) Depth: 3

起初我以为代码有问题,由于某种原因,它导致组件被多次解析。正如史蒂文指出的那样,当组件注册为InstancePerDependency时,可能会发生这种情况。但是由于我的几个组件都注册为InstancePerLifetimeSingleInstance依赖项,因此这些依赖项不应在图中两次解析。

Steven does mention表示,“ InstancePerDependency依赖关系的第一个解析似乎比下一个解析具有更多的依赖,因为此图仅显示了解析。也许这是正在发生的事情。”看到InstancePerLifetime组件多次注册,在整个图表中有几次,我感觉这里还有其他事情。

这里可能会发生什么?

如何注册依赖项

以下代码是我们用于注册程序集的代码:

public static void RegisterAssemblies(this ContainerBuilder containerBuilder, IList<Assembly> assemblies, params Type[] typesToExclude)
{
  if (containerBuilder != null && assemblies.Any())
  {
    var allTypes = assemblies.SelectMany(assembly => assembly.GetTypes()).Where(t => !typesToExclude.Any(t2 => t2.IsAssignableFrom(t))).ToList();
    RegisterAllClassesWithoutAttribute(containerBuilder, allTypes);

    RegisterClassesThatAreSingleton(containerBuilder, allTypes);

    RegisterClassesThatAreInstancePerLifetimeScope(containerBuilder, allTypes);

    RegisterGenericInterfaces(containerBuilder, allTypes);

    RegisterRealOrTestImplementations(containerBuilder, allTypes);

    RegisterAutofacModules(containerBuilder, allTypes);

    containerBuilder.Register(c => UnikCallContextProvider.CurrentContext).As<IUnikCallContext>();
  }
}

private static void RegisterAutofacModules(ContainerBuilder containerBuilder, List<Type> allTypes)
{
  var modules = allTypes.Where(type => typeof(IModule).IsAssignableFrom(type) && type.GetCustomAttribute<DoNotRegisterInIocAttribute>() == null);
  foreach (var module in modules)
  {
    containerBuilder.RegisterModule((IModule) Activator.CreateInstance(module));
  }
}

private static void RegisterRealOrTestImplementations(ContainerBuilder containerBuilder, List<Type> allTypes)
{
  if (StaticConfigurationHelper.UseRealImplementationsInsteadOfTestImplementations)
  {
    var realTypes = allTypes.Where(type => type.GetCustomAttribute<RealImplementationAsInstancePerLifetimeScopeAttribute>() != null).ToArray();
    containerBuilder.RegisterTypes(realTypes).AsImplementedInterfaces()
      .InstancePerLifetimeScope();
  }
  else
  {
    var testTypes = allTypes.Where(type => type.GetCustomAttribute<TestImplementationAsInstancePerLifetimeScopeAttribute>() != null).ToArray();
    containerBuilder.RegisterTypes(testTypes).AsImplementedInterfaces()
      .InstancePerLifetimeScope();
  }
}

private static void RegisterGenericInterfaces(ContainerBuilder containerBuilder, List<Type> allTypes)
{
  var typesAsGenericInterface = allTypes.Where(type => type.GetCustomAttribute<RegisterAsGenericInterfaceAttribute>() != null).ToArray();
  foreach (var type in typesAsGenericInterface)
  {
    var attribute = type.GetCustomAttribute<RegisterAsGenericInterfaceAttribute>();
    containerBuilder.RegisterGeneric(type).As(attribute.Type);
  }
}

private static void RegisterClassesThatAreInstancePerLifetimeScope(ContainerBuilder containerBuilder, List<Type> allTypes)
{
  var typesAsInstancePerDependency = allTypes.Where(type => type.GetCustomAttribute<InstancePerLifetimeScopeAttribute>() != null).ToArray();
  containerBuilder.RegisterTypes(typesAsInstancePerDependency).InstancePerLifetimeScope().AsImplementedInterfaces();
}

private static void RegisterClassesThatAreSingleton(ContainerBuilder containerBuilder, List<Type> allTypes)
{
  var typesAsSingleton = allTypes.Where(type => type.GetCustomAttribute<SingletonAttribute>() != null).ToArray();
  containerBuilder.RegisterTypes(typesAsSingleton).SingleInstance().AsImplementedInterfaces();
}

private static void RegisterAllClassesWithoutAttribute(ContainerBuilder containerBuilder, List<Type> allTypes)
{
  var types = allTypes.Where(type => !typeof(IModule).IsAssignableFrom(type) &&
                                     type.GetCustomAttribute<DoNotRegisterInIocAttribute>() == null &&
                                     type.GetCustomAttribute<SingletonAttribute>() == null &&
                                     type.GetCustomAttribute<RealImplementationAsInstancePerLifetimeScopeAttribute>() == null &&
                                     type.GetCustomAttribute<TestImplementationAsInstancePerLifetimeScopeAttribute>() == null &&
                                     type.GetCustomAttribute<InstancePerLifetimeScopeAttribute>() == null &&
                                     type.GetCustomAttribute<RegisterAsGenericInterfaceAttribute>() == null).ToArray();
  containerBuilder.RegisterTypes(types).AsSelf().AsImplementedInterfaces();
}

传递到RegisterAssemblies方法的程序集可以通过以下方式获取:

private List<Assembly> GetAssemblies()
{
  var assemblies = AssemblyResolveHelper.LoadAssemblies(AppDomain.CurrentDomain.BaseDirectory,
    new Regex(@"Usd.EA.*\.dll"),
    SearchOption.TopDirectoryOnly);
  assemblies.AddRange(AssemblyResolveHelper.LoadAssemblies(AppDomain.CurrentDomain.BaseDirectory,
    new Regex(@"Usd.Utilities.*\.dll"),
    SearchOption.TopDirectoryOnly));

  assemblies.Add(GetType().Assembly);
  return assemblies.Distinct().ToList();
}

属性

RegisterAllClassesWithoutAttribute中使用的属性是我们手动分配给各个类的自定义属性

using System;

[AttributeUsage(AttributeTargets.Class)]
public class DoNotRegisterInIocAttribute : Attribute
{
}

像这样使用

[ExcludeFromCodeCoverage]
[DoNotRegisterInIoc]
public sealed class TestClass : ITestClass

当我不覆盖自动传真MaxResolveDepth时出现以下错误

失败尝试创建类型为控制器时发生错误'BogfoerController'。确保控制器具有无参数公共构造函数。激活λ:Usd.EA时引发异常.Bogfoering.WebApi.Controllers.BogfoerController->Usd.EA.Bogfoering.WebApi.Controllers.BogfoerController-> ......工厂范围内的组件之间可能的循环依赖关系。链包括'Activator = DomainWrapper(DelegateActivator),服务=SomeService,生命周期= Autofac.Core.Lifetime.CurrentScopeLifetime,共享=无,所有权=外部拥有'

c# .net dependency-injection autofac ioc-container
1个回答
0
投票

简短回答:从调用ILifetimeScope创建的子BeginLifetimeScope(Action<ContainerBuilder> configurationAction)解决服务时,Autofac行为会导致这种情况。

长回答:我已经建立了一个简单的测试来证明上述说法。我生成了一个引用自己的51个测试类。

public class Test0
{
    public Test0() { }
}

public class Test1
{
    public Test1(Test0 test) { }
}

(...)

public class Test50
{
    public Test50(Test49 test) { }
}

将它们注册在新创建的容器中,并尝试直接从容器中解析“ Test50”类。正如您已经发现的那样。 Autofac库中有50个依赖项深度的硬编码限制,您可以在GitHub页面上看到它。达到此限制后,将抛出DependencyResolutionException,并指出“ 工厂范围内的组件之间可能的循环依赖关系。”而这正是我的第一个测试中发生的情况。

现在您已经问过,为什么会看到具有相同依赖项的多个注册。因此,这是有趣的部分。当您尝试解析实例时,可能要使用BeginLifetimeScope函数来创建新的ILifetimeScope。除非您打算使用重载之一向子作用域添加一些新的注册,否则这仍然可以。请参见下面的示例:

using (var scope = container.BeginLifetimeScope(b => { }))
{
    var test = scope.Resolve<Test49>();
}

我仅解决了50个依赖项(以前已经起作用),但是现在,它产生了一个异常:

Exception message

如您所见,这与您之前描述的行为完全相同。现在每个依赖项显示2次。在该图像上,您还可以看到依赖图仅达到了Test25类。这有效地将先前的最大深度减少了一半(总共25个依赖项!)。我们可以通过成功解析Test24类来对此进行测试,但是在尝试解析Test25时会引发异常。这变得更加有趣,您如何看待,如果我们添加另一个示波器会发生什么?

using (var scope1 = container.BeginLifetimeScope(b => { }))
{
    using (var scope2 = scope1.BeginLifetimeScope(b => { }))
    {
        var test2 = scope2.Resolve<Test49>();
    }
}

您可能已经猜到了,现在您只能解析深度50/3 =〜16的依赖项。

Yet another exception

结论:创建嵌套作用域限制了依赖关系图的实际可用最大深度N次,其中N是作用域的深度。老实说,在不扩展容器构建器的情况下创建的作用域不会影响此数字。在我看来,这是一个非常荒谬的事情,因为硬编码的幻数在文档中没有出现,无法轻松配置,甚至不能代表实际的最大深度,并且在溢出时会引发误导性异常,说明您在某处的图形中具有循环依赖性。

© www.soinside.com 2019 - 2024. All rights reserved.