Simple Injector - 具有瞬态依赖设计的单例
Simple Injector - Singleton with transient dependency design
背景
我知道 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<RockPlayer>
container.GetAllInstances<IPlayer>
container.GetAllInstances<ISongAware>
必须return同一个实例,所以IPlayer
注册必须是单例的。这样做,所有 PlayerEqualizer
也必须是单例,因为它们是单例注册的依赖项,但 PlayerEqualizer
实现不是单例(RockPlayer
和 RnBPlayer
两者取决于 RockPlayerEqualizer
但他们需要不同的实例)。
我试过的
我能找到的唯一解决方案是将 SimpleInjector container.Options.SuppressLifestyleMismatchVerification
标志设置为 False
但我不想失去该功能...另一个选项可以调用 SuppressDiagnosticWarning
方法在 IPlayer
的注册上,但尽管我无法让它工作,但我真正担心的是这些解决方案只是解决方法...
我是不是漏掉了什么?
您想要的是 而不是 将 PlayerEqualizer
个实例注册为 Transient but as Instance Per Dependency。
从技术上讲,这两种生活方式是相同的,因为它们都 return 在每个请求上创建新实例。然而,Instance per Dependency 的 intend 非常 不同,因为:
Each consumer will get a new instance of the given service type and that dependency is expected to get live as long as its consuming type.
虽然 Transient 的目的是短暂的依赖。
这种生活方式被故意排除在 Simple Injector 之外,因为:
its usefulness is very limited compared to the Transient lifestyle. It ignores lifestyle mismatch checks and this can easily lead to errors, and it ignores the fact that application components should be immutable. In case a component is immutable, it’s very unlikely that each consumer requires its own instance of the injected dependency.
然而,该项目的代码示例包含一个 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);
背景
我知道 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<RockPlayer>
container.GetAllInstances<IPlayer>
container.GetAllInstances<ISongAware>
必须return同一个实例,所以IPlayer
注册必须是单例的。这样做,所有 PlayerEqualizer
也必须是单例,因为它们是单例注册的依赖项,但 PlayerEqualizer
实现不是单例(RockPlayer
和 RnBPlayer
两者取决于 RockPlayerEqualizer
但他们需要不同的实例)。
我试过的
我能找到的唯一解决方案是将 SimpleInjector container.Options.SuppressLifestyleMismatchVerification
标志设置为 False
但我不想失去该功能...另一个选项可以调用 SuppressDiagnosticWarning
方法在 IPlayer
的注册上,但尽管我无法让它工作,但我真正担心的是这些解决方案只是解决方法...
我是不是漏掉了什么?
您想要的是 而不是 将 PlayerEqualizer
个实例注册为 Transient but as Instance Per Dependency。
从技术上讲,这两种生活方式是相同的,因为它们都 return 在每个请求上创建新实例。然而,Instance per Dependency 的 intend 非常 不同,因为:
Each consumer will get a new instance of the given service type and that dependency is expected to get live as long as its consuming type.
虽然 Transient 的目的是短暂的依赖。
这种生活方式被故意排除在 Simple Injector 之外,因为:
its usefulness is very limited compared to the Transient lifestyle. It ignores lifestyle mismatch checks and this can easily lead to errors, and it ignores the fact that application components should be immutable. In case a component is immutable, it’s very unlikely that each consumer requires its own instance of the injected dependency.
然而,该项目的代码示例包含一个 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);