I am trying to switch my DI container from Unity to SimpleInjector and I am struggling a bit with collections.
I have two concrete implementations of IExceptionLogWriter
that I want to register as singletons so I do the following:
container.RegisterSingleton<RaygunExceptionLogWriter>();
container.RegisterSingleton<SqlExceptionLogWriter>();
Now I want to register both of those with a collection so I do this:
container
.RegisterCollection<IExceptionLogWriter>(
new[] {
typeof(RaygunExceptionLogWriter),
typeof(SqlExceptionLogWriter) });
Now when I try run the application I get the following error:
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.
If I understand the error correctly it looks like I need to somehow register IExceptionLogWriter[]
as a singleton since I registered RaygunExceptionLogWriter
and SqlExceptionLogWriter
as singletons. Maybe I am being dense but I cannot for the life of me figure out how to do that.
I tried this:
container.RegisterSingleton<IExceptionLogWriter[]>();
That obviously crashed and burned.
Edit - All Pertinent Registrations
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) });
And the related constructors:
public SqlConnectionFactory(string connectionString)
public SqlApplicationExceptionRepository(SqlConnectionFactory connectionFactory)
public ErrorHandler(IExceptionLogger exceptionLogger)
public ExceptionLogger(IExceptionLogWriter[] exceptionLogWriters)
public RaygunExceptionLogWriter()
public SqlExceptionLogWriter(IApplicationExceptionRepository applicationExceptionRepository)
What is happening here is that your ExceptionLogger
component has a constructor argument of type IExceptionLogWriter[]
(an array of IExceptionLogWriter
). Simple Injector automatically resolves array for you, but the implicit registration that Simple Injector makes is transient. The reason for this is that:
So for safety reasons, Simple Injector makes the array registration transient. This allows the diagnostic system to kick in and warn you about any captive dependency it might cause.
The solution is simple and you have two options:
ExceptionLogger
consumer transient, orExceptionLogger
's dependency from IExceptionLogger[]
to either IEnumerable<IExceptionLogger>
, IList<IExceptionLogger>
, ICollection<IExceptionLogger>
, IReadOnlyList<IExceptionLogger>
or IReadOnlyCollection<IExceptionLogger>
.For these collection abstractions Simple Injector will inject a collection that: