Say I have a service that I want to resolve with a scoped lifetime. But sometime I try to resolve it as the interface type and sometimes at the implementation type.
The first thing I tried to do was this:
ServiceCollection services;
services.AddScoped<MyClass>();
services.AddScoped<IMyInterface, MyClass>();
The problem with the above sample is that a different instance is used if I resolve IMyInterface, and than resolve MyClass. Basically it's possible that 2 scoped instances are alife at the same time.
I work around this issue in the following way. But it's very error-prone because you can easily forget to do this at one place, and it's really hard to notice.
serviceCollection.AddScoped<MyClass>();
serviceCollection.AddScoped<IMyInterface, MyClass>(sp => sp.GetRequiredService<MyClass>());
Is there any way to accomplish what I want in a way that is less error prone. Preferrably, but not necessarily, in a single registration?
I.e. as an xUnit test:
public class Tests
{
[Fact]
public void ReturnsSameInstanceForImplementationAndServiceType()
{
var serviceCollection = new ServiceCollection();
// TODO: Change these lines so they're less error prone.
serviceCollection.AddScoped<MyClass>();
serviceCollection.AddScoped<IMyInterface, MyClass>(sp => sp.GetRequiredService<MyClass>());
var services = serviceCollection.BuildServiceProvider();
var myInt = services.GetRequiredService<IMyInterface>();
var myCls = services.GetRequiredService<MyClass>();
Assert.Equal(myCls, myInt);
}
class MyClass : IMyInterface { }
interface IMyInterface { }
}
One option would be to create your own extension method that wraps up the two lines you've shown in your question. For example:
public static class ServiceCollectionExtensions
{
public static void AddScopedInterfaceAndClass<TInterface, TClass>(this IServiceCollection serviceCollection)
where TInterface : class
where TClass : class, TInterface
{
serviceCollection.AddScoped<TClass>();
serviceCollection.AddScoped<TInterface, TClass>(sp => sp.GetRequiredService<TClass>());
}
}
You could call this like so:
serviceCollection.AddScopedInterfaceAndClass<IMyInterface, MyClass>();
I appreciate that AddScopedInterfaceAndClass isn't the perfect name - it's just an example to demonstrate the idea. Also, there is still the downside that you'd have to remember to use this extension rather then AddScoped.
Note: You could simplify the second AddScoped in the extension method by removing the second generic (TClass) as this is inferred by the compiler.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With