gpt4 book ai didi

c# - Autofac 多次注册组件

转载 作者:行者123 更新时间:2023-12-03 23:09:58 34 4
gpt4 key购买 nike

In a previous question about how 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

一开始我认为代码有问题,并且由于某种原因导致组件多次得到解决。正如 Steven 指出的那样,当组件注册为 InstancePerDependency 时,可能会发生这种情况。 .但是因为我的几个组件注册为 InstancePerLifetimeSingleInstance依赖关系,这些依赖关系不应在图中解决两次。

Steven does mentionInstancePerDependency 依赖项的第一个解析似乎比下一个解析具有更多的依赖关系,因为此图仅显示解析。也许这就是正在发生的事情。”但正如我所见 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

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

Failed An error occurred when trying to create a controller of type 'BogfoerController'. Make sure that the controller has a parameterless public constructor. An exception was thrown while activating λ:Usd.EA .Bogfoering.WebApi.Controllers.BogfoerController -> Usd.EA.Bogfoering.WebApi.Controllers.BogfoerController -> ...... Probable circular dependency between factory-scoped components. Chain includes 'Activator = DomainWrapper (DelegateActivator), Services = SomeService, Lifetime = Autofac.Core.Lifetime.CurrentScopeLifetime, Sharing = None, Ownership = ExternallyOwned'

最佳答案

简答:
这是由 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 上看到它。页。达到此限制后 DependencyResolutionException抛出“工厂范围组件之间可能的循环依赖”。这正是我第一次测试中发生的情况。

现在您已经问过,为什么您会看到相同依赖项的多个注册。所以有趣的部分来了。当您尝试解析您的实例时,您可能会使用 BeginLifetimeScope函数来创建新的 ILifetimeScope。这仍然可以,除非您要使用重载之一向子作用域添加一些新注册。请参阅下面的示例:
using (var scope = container.BeginLifetimeScope(b => { }))
{
var test = scope.Resolve<Test49>();
}

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

Exception message

如您所见,这与您之前描述的行为完全相同。每个依赖项现在显示 2 次。在该图像上,您还可以看到依赖关系图仅达到了 Test25类(class)。这有效地将之前的最大深度减少了一半(整个 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 是范围的深度。老实说,在没有扩展容器构建器的情况下创建的范围不会影响这个数字。在我看来,这是一个巨大的荒谬,有硬编码的魔数(Magic Number),在文档中没有任何地方,无法轻松配置,甚至不代表实际的最大深度,并且当溢出时,它会抛出误导性异常,说明您在图中某处具有循环依赖关系。

解决方案:作为此问题的解决方案,您不能使用此函数的此重载。由于架构限制,甚至可能使用 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 年提到的,他们甚至无法在这个日期之前更改该异常消息。

关于c# - Autofac 多次注册组件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59305813/

34 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com