SimpleInjector.ActivationException 由于 collection 生活方式

SimpleInjector.ActivationException due to collection lifestyle

我正在尝试将我的 DI 容器从 Unity 切换到 SimpleInjector,我在 collections 上遇到了一些困难。

我有两个 IExceptionLogWriter 的具体实现,我想注册为单例,所以我执行以下操作:

container.RegisterSingleton<RaygunExceptionLogWriter>();
container.RegisterSingleton<SqlExceptionLogWriter>();

现在我想用 collection 注册这两个,所以我这样做:

container
.RegisterCollection<IExceptionLogWriter>(
    new[] { 
        typeof(RaygunExceptionLogWriter), 
        typeof(SqlExceptionLogWriter) });

现在,当我尝试 运行 应用程序时,出现以下错误:

Additional information: A lifestyle mismatch is encountered. ExceptionLogger (Singleton) depends on IExceptionLogWriter[] (Transient). Lifestyle mismatches can cause concurrency bugs in your application. Please see https://simpleinjector.org/dialm to understand this problem and how to solve it.

如果我正确理解错误,看起来我需要以某种方式将 IExceptionLogWriter[] 注册为单例,因为我将 RaygunExceptionLogWriterSqlExceptionLogWriter 注册为单例。也许我很笨,但我一辈子都想不出该怎么做。

我试过这个:

container.RegisterSingleton<IExceptionLogWriter[]>();

那明显是坠机烧毁了

编辑 - 所有相关注册

container.RegisterSingleton<SqlConnectionFactory>(new SqlConnectionFactory(connectionString));
container.RegisterSingleton<IApplicationExceptionRepository, SqlApplicationExceptionRepository>();
container.RegisterSingleton<IErrorHandler, ErrorHandler>();
container.RegisterSingleton<IExceptionLogger, ExceptionLogger>();
container.RegisterSingleton<RaygunExceptionLogWriter>();
container.RegisterSingleton<SqlExceptionLogWriter>();
container
.RegisterCollection<IExceptionLogWriter>(
    new[] { 
        typeof(RaygunExceptionLogWriter), 
        typeof(SqlExceptionLogWriter) });

以及相关的构造函数:

public SqlConnectionFactory(string connectionString)
public SqlApplicationExceptionRepository(SqlConnectionFactory connectionFactory)
public ErrorHandler(IExceptionLogger exceptionLogger)
public ExceptionLogger(IExceptionLogWriter[] exceptionLogWriters)
public RaygunExceptionLogWriter()
public SqlExceptionLogWriter(IApplicationExceptionRepository applicationExceptionRepository)

这里发生的事情是您的 ExceptionLogger 组件有一个类型为 IExceptionLogWriter[] 的构造函数参数(IExceptionLogWriter 的数组)。 Simple Injector 会自动为您解析数组,但 Simple Injector 进行的隐式注册是暂时的。这样做的原因是:

  • 数组是引用列表。如果列表包含瞬态组件,则将数组作为单例注入将导致 Captive Dependencies.
  • 数组是可变类型;每个人都可以更改数组的内容。使其成为单例可能会导致应用程序的不相关部分因数组更改而失败。

因此,出于安全原因,Simple Injector 使数组注册成为瞬态。这允许诊断系统启动并警告您它可能导致的任何依赖性。

解决方法很简单,您有两个选择:

  1. 使 ExceptionLogger 消费者瞬态,或
  2. ExceptionLogger 的依赖性从 IExceptionLogger[] 更改为 IEnumerable<IExceptionLogger>IList<IExceptionLogger>ICollection<IExceptionLogger>IReadOnlyList<IExceptionLogger>IReadOnlyCollection<IExceptionLogger>.

对于这些集合抽象,Simple Injector 将注入一个集合:

  • 无法从外部更改,并且
  • 通过在每次迭代集合时根据其生活方式解析元素来防止俘虏依赖。