gpt4 book ai didi

c# - 为什么指定生命周期管理器时类型会注册两次?

转载 作者:IT王子 更新时间:2023-10-29 04:19:41 26 4
gpt4 key购买 nike

我在以下场景中使用 Unity 的按约定注册机制:

public interface IInterface { }

public class Implementation : IInterface { }

给定 Implementation 类及其接口(interface),我按以下方式运行 RegisterTypes:

unityContainer.RegisterTypes(
new[] { typeof(Implementation) },
WithMappings.FromAllInterfaces,
WithName.Default,
WithLifetime.ContainerControlled);

此调用后,unitContainer 包含三个注册:

  • IUnityContainer -> IUnityContainer(确定)
  • IInterface -> 实现 (ok)
  • 实现 -> 实现 (???)

当我按如下方式更改调用时:

unityContainer.RegisterTypes(
new[] { typeof(Implementation) },
WithMappings.FromAllInterfaces,
WithName.Default);

容器只包含两个注册:

  • IUnityContainer -> IUnityContainer(确定)
  • IInterface -> 实现 (ok)

(这是期望的行为)。

窥视Unity's source code后,我注意到对于 IUnityContainer.RegisterType 应该如何工作存在一些误解。

RegisterTypes 方法的工作原理如下(注释指出了上述场景中的值):

foreach (var type in types)
{
var fromTypes = getFromTypes(type); // { IInterface }
var name = getName(type); // null
var lifetimeManager = getLifetimeManager(type); // null or ContainerControlled
var injectionMembers = getInjectionMembers(type).ToArray(); // null

RegisterTypeMappings(container, overwriteExistingMappings, type, name, fromTypes, mappings);

if (lifetimeManager != null || injectionMembers.Length > 0)
{
container.RegisterType(type, name, lifetimeManager, injectionMembers); // !
}
}

因为fromTypes不为空,RegisterTypeMappings添加了一个类型映射:IInterface -> Implementation(正确).

然后,如果 lifetimeManager 不为空,代码会尝试通过以下调用更改生命周期管理器:

container.RegisterType(type, name, lifetimeManager, injectionMembers);

这个函数的名字完全是误导,因为the documentation明确指出:

RegisterType a LifetimeManager for the given type and name with the container. No type mapping is performed for this type.

不幸的是,不仅名称具有误导性,而且文档也有误。在调试这段代码时,我注意到,当没有来自 type 的映射时(Implementation 在上述场景中),它被添加(作为 type -> type),这就是我们在第一个场景中以三个注册结束的原因。

我已经下载了 Unity 的源代码来解决这个问题,但我发现了以下单元测试:

[TestMethod]
public void RegistersMappingAndImplementationTypeWithLifetimeAndMixedInjectionMembers()
{
var container = new UnityContainer();
container.RegisterTypes(new[] { typeof(MockLogger) }, getName: t => "name", getFromTypes: t => t.GetTypeInfo().ImplementedInterfaces, getLifetimeManager: t => new ContainerControlledLifetimeManager());

var registrations = container.Registrations.Where(r => r.MappedToType == typeof(MockLogger)).ToArray();

Assert.AreEqual(2, registrations.Length);

// ...

- 这几乎正是我的情况,并引出了我的问题:

为什么会这样?这是概念上的错误,是为了匹配现有行为但不一定正确而创建的单元测试,还是我遗漏了一些重要的东西?

我使用的是 Unity v4.0.30319。

最佳答案

我们需要联系原始开发人员才能确定,但​​这是我只能假设为什么它被编码为这种方式...

Unity 具有允许解析具体类的功能,即使该类型尚未注册。在这种情况下,Unity 必须假设您需要默认生命周期管理器( transient )并且该具体类型没有注入(inject)成员。如果您不喜欢这些默认策略,那么您需要自己注册具体类型并指定您的自定义。

因此,按照这个思路,当您调用 RegisterTypes 并且您确实指定了生命周期管理器和/或注入(inject)成员时,Unity 会假设您在通过接口(interface)和具体类型解析时需要该行为。但是,如果您不指定这些策略,则 Unity 不需要具体注册,因为它会在解析具体类型时回退到默认行为。

我能想到的唯一其他解释是为了插件。 InjectionMember 是可扩展的,因此 Unity 认为您可以为插件传递自定义行为。因此,它假设您可能希望在按照约定注册时将插件的自定义行为应用于界面和具体。


我了解到您在尝试对应用程序 Bootstrap 进行单元测试时遇到了这个问题。我假设您正在测试以确保您注册的类型并且仅注册这些类型。这会破坏您的测试,因为某些测试会发现这种额外的具体注册。

从单元测试的角度来看,我认为您超出了您尝试测试的范围。如果您开始使用插件,将会有很多其他注册开始出现(例如拦截策略和行为)。由于您的测试方式,这只会增加您现在看到的问题。我建议您更改测试以确保只注册白名单类型并忽略其他所有内容。拥有具体的(或其他额外的注册)对您的应用程序是有益的。

(例如,将您的接口(interface)放入白名单并断言每个接口(interface)都已注册,而忽略具体已注册的事实)

关于c# - 为什么指定生命周期管理器时类型会注册两次?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34906500/

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