统一循环引用注释对象

本文关键字:注释 对象 引用 循环 | 更新日期: 2023-09-27 18:37:22

我正在尝试使用遗留应用程序(带有源代码)来使用 Unity 实现 IoC。我目前面临的问题是,我有 2 个相互循环引用的类。

A 类参考 B 类B 类参考 A 类

我通过使用带有注释的属性设置器注入每个依赖项;

On class A:
[Dependency]
public IServiceA ServiceA { get; set; }

On class B:
[Dependency]
public IServiceB ServiceB { get; set; }

因此,Unity 将进入循环引用。有没有办法绕过(除了重构里面的方法),因为这些类是遗留的,我不想花太多精力来改变它们的设计?

统一循环引用注释对象

当您

尝试解析 A 时,Unity 将执行此步骤。

Create A
Look at Properties for A and create them
Found B will create B
Look at Properties for B and create them
Found A will Create A

你陷入了一个循环。

最好的选择是创建 A 和 B 所依赖的 C。 但是,C 不依赖于 A 或 B。

我同意重构是最佳实践,但是您可以通过更改要Lazy<T>的一个或两个属性来实现您所追求的。 然后,Unity 不会解析另一个实例,直到您使用该属性。

On class A:
[Dependency]
public Lazy<IServiceA> ServiceA { get; set; }

On class B:
[Dependency]
public Lazy<IServiceB> ServiceB { get; set; }

或者,您可以更改其注册生存期,以便它们重复使用相同的实例。 如果您不希望共享相同的实例,这可能对您不起作用。 也许PerResolveLifetime会起作用?

  1. 解析 A(实例化 A)
  2. 检测 B 依赖关系
  3. 解析 B(实例化 B)
  4. 检测依赖关系
  5. 解析 A(由于生存期而重用实例 A,并完成递归)

例:

container.RegisterType<A>(new PerResolveLifetimeManager());

这确实是 Unity 的一个令人沮丧的限制。但是,有一个 2 遍解决方案:

[TestClass]
public class UnityTests
{
    [TestMethod]
    public void Cure_For_UnityNotBeingVerySmartAtBindingCircularDependentProperties()
    {
        var container = new UnityContainer();
        container.RegisterType<ISharedResource, SharedResource>(new ContainerControlledLifetimeManager());
        container.RegisterType<ICycleA, CycleA>(new ContainerControlledLifetimeManager(), new InjectionProperty("SharedResource"));
        container.RegisterType<ICycleB, CycleB>(new ContainerControlledLifetimeManager(), new InjectionProperty("SharedResource"));
        var a = container.Resolve<ICycleA>();
        var b = container.Resolve<ICycleB>();
        container.RegisterType<ICycleA, CycleA>("buildup", new ContainerControlledLifetimeManager());
        container.RegisterType<ICycleB, CycleB>("buildup", new ContainerControlledLifetimeManager());
        container.BuildUp(a, "buildup");
        container.BuildUp(b, "buildup");
        Assert.IsInstanceOfType(a, typeof(CycleA));
        Assert.IsInstanceOfType(a.Dependency, typeof(CycleB));
        Assert.AreSame(a, a.Dependency.Dependency);
    }
}
internal interface ISharedResource { }
class SharedResource : ISharedResource { }
class CycleB : ICycleB
{
    [Dependency]public ISharedResource SharedResource { get; set; }
    [Dependency]public ICycleA Dependency { get; set; }
}
class CycleA : ICycleA
{
    [Dependency]public ISharedResource SharedResource { get; set; }
    [Dependency]public ICycleB Dependency { get; set; }
}
interface ICycleB
{
    [Dependency]ISharedResource SharedResource { get; set; }
    [Dependency]ICycleA Dependency { get; set; }
}
interface ICycleA
{
    [Dependency]ISharedResource SharedResource { get; set; }
    [Dependency]ICycleB Dependency { get; set; }
}

它利用注入属性重载,该重载告诉 Unity 仅连接指定的依赖项。

如果您的类具有共享资源(例如日志记录服务),请在第一次传递时注册此依赖项,最后的 BuildUp 传递将所有类连接在一起。

它确实需要在所有类上调用 Resolve 和 BuildUp,有点尴尬,但可以使用 UnityContainer 扩展相当容易地实现自动化。

这应该由Microsoft解决,因为它是一个相当标准的用例。