【问题标题】:Simple Injector - Singleton with transient dependency designSimple Injector - 具有瞬态依赖设计的单例
【发布时间】:2017-12-04 00:41:22
【问题描述】:

上下文

我知道 SimpleInjector 的 LifestyleMismatch 异常的目的以及它抛出它的原因。但假设有:

Players.dll

public abstract class PlayerEqualizer { ... }

public abstract class Player : IPlayer, ISongAware
{
    public Player(PlayerEqualizer eq)
    {
        Equalizer = eq;
    }

    public PlayerEqualizer Equalizer { get; }
    public abstract void StartPlay();
}

Players.Rock.dll

public class RockPlayerEqualizer  : PlayerEqualizer {}

public class RockPlayer : Player
{
    public RockPlayer(RockPlayerEqualizer  eq) : base(eq) {}

    public override void StartPlay() { ... }
}

public class RnBPlayer : Player
{
    public RnBPlayer(RockPlayerEqualizer  eq) : base(eq) {}

    public override void StartPlay() { ... }
}

Players.Pop.dll

public class PopPlayerEqualizer : PlayerEqualizer{}

public class PopPlayer : Player
{
    public PopPlayer(PopPlayerEqualizer eq) : base(eq) {}

    public override void StartPlay() { ... }
}

Player 的所有实现都注册为IPlayer 的集合,并且所有注册都是单例的:

var registrations = container
    .GetTypesToRegister(typeof(IPlayer), assemblies)
    .Select(t => Lifestyle.Singleton.CreateRegistration(t, container));


foreach (var registration in registrations)
{
    container.AddRegistration(registration.ImplementationType, registration);
}

container.RegisterCollection<IPlayer>(registrations);
container.RegisterCollection<ISongAware>(container.GetCurrentRegistrations()
    .Where(ip => typeof(ISongAware).IsAssignableFrom(ip.ServiceType))
    .Select(ip => ip.Registration));

问题

全部

  • container.GetInstance&lt;RockPlayer&gt;
  • container.GetAllInstances&lt;IPlayer&gt;
  • container.GetAllInstances&lt;ISongAware&gt;

必须返回相同的实例,因此IPlayer 注册必须是单例的。这样做,所有 PlayerEqualizer 也必须是单例的,因为它们是单例注册的依赖项,但 PlayerEqualizer 实现不是单例(RockPlayerRnBPlayer 都依赖于 RockPlayerEqualizer 但它们需要不同的实例)。

我尝试了什么

我能找到的唯一解决方案是将 SimpleInjector container.Options.SuppressLifestyleMismatchVerification 标志设置为 False 但我不想失去该功能...另一个选项可以在 IPlayer 上调用 SuppressDiagnosticWarning 方法的注册,但尽管我无法让它工作,但我真正担心的是这些解决方案只是解决方法......

我错过了什么吗?

【问题讨论】:

  • 这些容器方法必须总是返回相同的实例吗? (这听起来像x-y problem,因为所有后果都来自该限制。)

标签: c# wpf inversion-of-control simple-injector


【解决方案1】:

您想要不是PlayerEqualizer 实例注册为Transient,而是注册为Instance Per Dependency

从技术上讲,这两种生活方式是相同的,因为它们都会在每次请求时返回新实例。但是,每个依赖项的实例意图非常不同的,因为:

每个消费者都将获得给定服务类型的一个新实例,并且只要其消费类型,该依赖项就会生效。

Transient 的意图是依赖是短暂的。

Simple Injector 故意忽略了这种生活方式,因为:

与 Transient 生活方式相比,它的用处非常有限。它忽略了生活方式不匹配检查,这很容易导致错误,并且它忽略了应用程序组件应该是不可变的事实。如果组件是不可变的,那么每个消费者都不太可能需要自己的注入依赖项实例。

然而,该项目的代码示例包含 InstancePerDependencyLifestyle 的定义,它可以执行您希望它执行的操作:

  • 它为每个消费者提供了自己的实例
  • 它会忽略注册时生活方式不匹配的情况,因为该实例的寿命预计与其消费者一样长

您可以按如下方式使用这种生活方式:

container.Register<RockPlayerEqualizer>(new InstancePerDependencyLifestyle());

更新

请注意,您的配置可以简化为以下内容:

var playerTypes = var registrations = container
    .GetTypesToRegister(typeof(IPlayer), assemblies);

foreach (Type playerType in playerTypes)
{
    container.Register(playerType, Lifestyle.Singleton);
}

container.RegisterCollection<IPlayer>(assemblies);
container.RegisterCollection<ISongAware>(assemblies);

【讨论】:

  • 谢谢史蒂文。但我想知道是否有比摆脱生活方式不匹配检查更好的方法来处理这种情况......
  • 视情况而定。一般来说,您应该更喜欢您的组件是无状态的,并通过它们的方法调用移动运行时数据。这通常会大大简化应用程序,并且更容易推理您的代码。您遇到此问题的原因是您的 PlayerEqualizer 实例是有状态的。但是,在您的情况下创建无状态设计是否有意义,我无法确定。
猜你喜欢
  • 1970-01-01
  • 2017-06-18
  • 1970-01-01
  • 2021-01-14
  • 2013-11-26
  • 2015-04-02
  • 1970-01-01
  • 1970-01-01
  • 2016-09-01
相关资源
最近更新 更多