Autofac多次注册组件

2023-12-31

在上一个关于如何可视化依赖关系图的问题中 https://stackoverflow.com/a/59247007/1955317我为现在用来可视化我的依赖关系图的代码奠定了基础,因为它是由 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

一开始我认为代码有问题,并且由于某种原因导致组件多次得到解决。正如 Steven 指出的,当组件注册为InstancePerDependency。但由于我的几个组件被注册为InstancePerLifetime or SingleInstance依赖关系,这些依赖关系不应在图中解析两次。

史蒂文确实提到 https://stackoverflow.com/questions/59246792/visualizing-dependency-tree-depth-in-autofac/59247007#comment104809677_59247007 that "第一个决心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

当我不覆盖 Autofacs 时MaxResolveDepth我收到以下错误

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


简短回答:这是由解析子级服务时 Autofac 行为引起的ILifetimeScope通过调用创建BeginLifetimeScope(Action<ContainerBuilder> configurationAction).

长答案:我设置了一个简单的测试来证明上述说法。我已经生成了 51 个引用自身的测试类。

public class Test0
{
    public Test0() { }
}

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

(...)

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

将它们注册到新创建的容器中,并尝试直接从容器解析“Test50”类。正如你已经发现的那样。 Autofac 库中存在 50 个依赖项深度的硬编码限制,您可以在GitHub https://github.com/bstewart00/Autofac/blob/develop/src/Autofac/Core/Resolving/CircularDependencyDetector.cs页。达到此限制后DependencyResolutionException被抛出并声明“工厂范围的组件之间可能存在循环依赖。“这正是我第一次测试中发生的情况。

现在您可能会问,为什么会看到相同依赖项的多个注册。那么有趣的部分来了。当您尝试解析实例时,您可能会使用BeginLifetimeScope函数创建新的 ILifetimeScope。这仍然没问题,除非您要使用其中一个重载向子作用域添加一些新的注册。请参阅下面的示例:

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

我只解决了 50 个依赖项(以前有效),但现在它产生了一个异常:

正如您所看到的,这与您之前描述的行为完全相同。每个依赖项现在显示 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 的依赖关系。

结论:创建嵌套作用域将依赖关系图的实际可用最大深度限制为 N 倍,其中 N 是作用域的深度。老实说,在不扩展容器构建器的情况下创建的范围不会影响这个数字。在我看来,这是一个巨大的荒谬,拥有硬编码的幻数,它在文档中没有任何地方,无法轻松配置,甚至不代表实际的最大深度,并且当溢出时,它会抛出误导性的异常,表明您图中某处存在循环依赖关系。

解决方案:作为此问题的解决方案,您无法使用此函数的此重载。由于架构限制,甚至可能使用 Autofac 作为 DI 容器的第 3 方框架,这可能是不可能的。

您已经提到的另一个解决方案是使用脏反射覆盖 MaxResolveDepth。

string circularDependencyDetectorTypeName = typeof(IContainer).AssemblyQualifiedName.Replace(typeof(IContainer).FullName, "Autofac.Core.Resolving.CircularDependencyDetector");
Type circularDependencyDetectorType = Type.GetType(circularDependencyDetectorTypeName);
FieldInfo maxResolveDepthField = circularDependencyDetectorType.GetField("MaxResolveDepth", BindingFlags.Static | BindingFlags.NonPublic);

maxResolveDepthField.SetValue(null, 500);

在 Autofac 的 GitHub 上,您还可以了解到他们已经计划改变CircularDependencyDetector,因此它可以处理无限深度的依赖关系,但这些计划是在 2018 年提到的,他们甚至无法在这个日期之前更改该异常消息。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Autofac多次注册组件 的相关文章

随机推荐

  • 如何在 C# 中将我自己的 wsdl 包含在我的 Web 服务中

    我有一个 wsdl 文件 我的 Web 服务 旧的 asmx 样式 必须实现它 这已得到照顾 当我发布 Web 服务时 您可以使用 wsdl 参数调用它以获取生成的 wsdl 如何包含我的 wsdl 文件 以便返回该文件而不是生成的文件 是
  • 语法错误:找不到或无法读取要导入的文件:foundation/common/ratios

    我安装了 Foundation 当我编辑并保存 app scss 文件时 它会在项目的 sass 文件夹中创建一个 app css 和一个 config rb 文件 当我打开 app css 文件时我得到这个 Syntax error Fi
  • 按对象的 NSDate 属性对对象数组进行排序[重复]

    这个问题在这里已经有答案了 可能的重复 如何对其中包含自定义对象的 NSMutableArray 进行排序 https stackoverflow com questions 805547 how to sort an nsmutablea
  • 如何使用nodejs/express上传和读取文件

    有各种各样关于此的帖子 但我仍然不明白 我想上传 csv 并读取和处理其内容 我的玉文件是这个 views import jade extends layout block content h1 title form action impo
  • 扩展 Google 地图 + D3 示例以添加路径或线路

    我正在尝试修改这个Google 地图 D3 示例 http bl ocks org 1125458包括点之间的线 我采取的方法是为每行添加新的 svg 元素 我成功创建了一组新的 svg 元素 var markerLink layer se
  • Java 字符串参数

    我来自 net 背景 想知道创建返回布尔值并修改通过参数传入的字符串的方法的可接受方式 我知道字符串在 Java 中是不可变的 因此下面的代码片段将始终生成一个空字符串 我只能返回布尔值 不能抛出异常 如果我需要将 String 类包装在
  • 301 重定向在 WordPress 中无法通过 .htaccess 工作

    我尝试在 WordPress 中的 htaccess 文件中应用两个 301 重定向 但收到 404 错误 这是我所拥有的 BEGIN WordPress
  • C++ 参数协方差

    我想知道为什么 C 不支持参数的协方差 如下例所示 或者是否有办法实现它 class base public virtual base func base ptr return new base class derived public b
  • C#中抽象类不能被密封?

    我在某处读过 抽象和密封修饰符相当于静态类 我还发现 当你声明一个静态类时 编译器会在内部将该类标记为抽象和密封 并在 IL 代码中创建一个私有构造函数 所以 我决定这样做 static class A public static void
  • Vue.js:vuex 操作中未捕获的承诺

    我了解 vuex actions 返回承诺 但我还没有找到处理错误的理想模式在 vuex 中 我当前的方法是在我的 axios 插件上使用错误拦截器 然后将错误提交到我的 vuex 存储 in 插件 axios js export defa
  • System.Net.FtpClient openwrite 不会上传文件,除非我在退出前插入睡眠

    我正在使用 System Net FtpClient 程序集将文件上传到测试 FTP 站点 当我运行下面的代码时 文件不会出现在远程位置 除非我按照下面的方式使用 Thread Sleep 我不想使用 using System using
  • 如何知道哪个列表项对用户可见?

    我通过我的应用程序录制了视频 并将其存储在存储卡中 在我获取这些视频并将其添加到列表视图后 根据屏幕尺寸 用户只能看到一个视频 如果用户上下滚动并停止 那时我想知道哪一个在用户面前 如果有人知道解决方案请帮助我 或任何其他方法 提前致谢 好
  • Apache Lucene TokenStream 合同违规

    使用 Appache Lucene TokenStream 删除停用词 导致错误 TokenStream contract violation reset close call missing reset called multiple t
  • Ceres Solver:无法禁用日志记录(google glog)

    我在一个项目中使用 ceres 求解器 当我调用ceres Solve函数后 库开始输出如下行 iterative schur complement solver cc 88 No parameter blocks left in the
  • 根据另一个 NSArray 字符串的排序对自定义对象的 NSArray 进行排序

    我有两个NSArray我想要以相同方式排序的对象 一个包含NSString对象 其他自定义Attribute对象 这是我的 关键 NSArray 的样子 The master order NSArray stringOrder NSArra
  • spring-boot 与 tomcat 和 cxf-servlet

    我正在尝试使用 spring boot 来建立嵌入式 Tomcat 我想在应用程序中使用 CXF 来提供一组 Web 服务 但我不知道如何建立 CXF servlet 我的主课看起来像这样 Configuration EnableAutoC
  • 在 Android 中使用相机活动

    如果您想使用使用本机 Android 相机的内置相机 Activity 只需执行以下操作即可 Intent camera new Intent MediaStore ACTION IMAGE CAPTURE this startActivi
  • 为什么我们仍然在 .NET 中使用数据集?

    数据集是 NET 1 0 中最重要的东西之一 即使现在使用 NET 3 5 我仍然发现自己必须使用它们 特别是当我必须调用一个返回数据集的存储过程时 我最终会得到该数据集手动转换为对象以使其更易于使用 我从来没有真正喜欢过数据集 并且发现它
  • django 将 .values_list('datetimefield') 转换为日期

    我想将带有日期时间对象的 value list 字段转换为日期对象 values list time finished flat True 给我 2016 03 22T18 52 53 486Z 我想要的是 2016 03 22 谢谢你 您
  • Autofac多次注册组件

    在上一个关于如何可视化依赖关系图的问题中 https stackoverflow com a 59247007 1955317我为现在用来可视化我的依赖关系图的代码奠定了基础 因为它是由 Autofac 解析的 运行代码 我得到一棵树 生成