Assuming I have this service registration:
services.AddScoped<IFoo, Foo>();
I know you can "Replace service registration in ASP.NET Core built-in DI container?" but is it possible to replace it but still keep the former instance intact like this?
services.AddScoped<IFoo>(sp => new MyFooWrapper(/* I need the original Foo here */));
This would be helpful in case I am performing a unit test where I can decide if I want to replace, for example, an external API call or not:
public class MyFooWrapper(IFoo wrapped) : IFoo
{
public void DoSomething()
{
if (TestingWithExternal)
{
wrapped.DoSomething(); // Original method that calls external HTTP API
}
else
{
FakeSomething();
}
}
}
Another use-case is that I want to wrap Blazor's IJSRuntime
since MS makes it very difficult to change its behaviors as the implementation is internal
and re-implement everything is just very difficult in case they update it.
Thanks to the answer, this is the code that can replace multiple services as well
// Replace with fakes
foreach (var s in services.ToList())
{
// Other logic
if (s.ServiceType == typeof(IOaApiService))
{
services.Remove(s);
var implType = s.ImplementationType;
ArgumentNullException.ThrowIfNull(implType);
services.Add(new(implType, implType, s.Lifetime));
services.Add(new(
s.ServiceType,
sp => new FakeOaAiService((IOaApiService)sp.GetRequiredService(implType)),
s.Lifetime));
}
}
You can register wrapped Foo
service as itself, i.e.:
service.AddScoped<Foo>();
Then, when registering wrapper class, use ServiceProvider
to get registered instance:
services.AddScoped<IFoo>(sp => new MyFooWrapper(sp.GetRequiredService<Foo>()));
This way, you will replace entirely all Foo
with its wrapper. If you want to still use both implementations, you have to separate them at interface level (define IFooWrapper
) or use keyed services (but it's latest feature in .NET).