Why it matters
every closed generic instance inherits the lifetime mismatch.
If the recipe is wrong at the top of the cookbook, every dish made from it comes out wrong.
DI009
open generic singleton registrations that depend on shorter-lived services.
Why it matters
every closed generic instance inherits the lifetime mismatch.
If the recipe is wrong at the top of the cookbook, every dish made from it comes out wrong.
Install
dotnet add package DependencyInjection.Lifetime.Analyzers --version 2.2.2
README problem example
services.AddScoped<IScopedService, ScopedService>();
services.AddSingleton(typeof(IRepository<>), typeof(Repository<>));
public sealed class Repository<T> : IRepository<T>
{
public Repository(IScopedService scoped) { }
}
README better pattern
services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
Repo sample extraction
Sample app warning case
public class BadRepository<T> : IBadRepository<T>
{
private readonly IScopedService _scopedService;
// DI009 will be reported on the registration line for this constructor
public BadRepository(IScopedService scopedService)
{
_scopedService = scopedService;
}
public T? Get(int id)
{
#pragma warning disable DI007 // Intentional use of service locator in sample
_scopedService.DoWork();
#pragma warning restore DI007
return default;
}
public void Save(T entity)
{
#pragma warning disable DI007 // Intentional use of service locator in sample
_scopedService.DoWork();
#pragma warning restore DI007
}
}
Sample app safe pattern
public class GoodScopedRepository<T> : IGoodScopedRepository<T>
{
private readonly IScopedService _scopedService;
public GoodScopedRepository(IScopedService scopedService)
{
_scopedService = scopedService;
}
public T? Get(int id)
{
#pragma warning disable DI007 // Intentional use of service locator in sample
_scopedService.DoWork();
#pragma warning restore DI007
return default;
}
}
Related guides
More documentation