I have a bunch of abstract classes with the base inheriting from an interface, as such :
public abstract class ServiceBase<T> : IServiceBase
protected ServiceBase(IExtService1 extService1, IExtService2 extService2, IExtService3 extService3)
public abstract class ServiceA<T> : ServiceBase<T>
protected ServiceA(IExtService1 extService1, IExtService2 extService2, IExtService3 extService3)
: base(extService1, extService2, extService3)
public abstract class ServiceAA : ServiceA<Type1>
protected ServiceAA(IExtService1 extService1, IExtService2 extService2, IExtService3 extService3)
: base(extService1, extService2, extService3)
public abstract class ServiceAB : ServiceA<Type2>
protected ServiceAB(IExtService1 extService1, IExtService2 extService2, IExtService3 extService3)
: base(extService1, extService2, extService3)
public abstract class ServiceB<T> : ServiceBase<Type3>
protected ServiceB(IExtService1 extService1, IExtService2 extService2, IExtService3 extService3)
: base(extService1, extService2, extService3)
public abstract class ServiceBA : ServiceB<Type1>
protected ServiceBA(IExtService1 extService1, IExtService2 extService2, IExtService3 extService3)
: base(extService1, extService2, extService3)
public abstract class ServiceBB : ServiceB<Type2>
protected ServiceAA(IExtService1 extService1, IExtService2 extService2, IExtService3 extService3)
: base(extService1, extService2, extService3)
with DI setup like this :
...
builder.Services.AddScoped<IExtService1, extService1>
builder.Services.AddScoped<IExtService2, extService2>
builder.Services.AddScoped<IExtService3, extService3>
builder.Services.AddScoped<IServiceBase, ServiceAA>
builder.Services.AddScoped<IServiceBase, ServiceAB>
builder.Services.AddScoped<IServiceBase, ServiceBA>
builder.Services.AddScoped<IServiceBase, ServiceBB>
...
now I would like to have access to ServiceBA in ServiceA or, if not possible, in ServiceAA and ServiceAB, like this :
public abstract class ServiceA<T> : ServiceBase<T>
protected ServiceA(IExtService1 extService1, IExtService2 extService2, IExtService3 extService3, ServiceBA<T> serviceBA)
: base(extService1, extService2, extService3)
But it seems that serviceBA in ServiceA isn't instanciated and is always null ... How can I fix that?
Your services are registered in the DI-container by their interface, not the implementation type, so the DI-container cannot retrieve the ServiceBA
by its type. But you can register services not only by interface:
builder.Services.AddScoped<IExtService1, extService1>
builder.Services.AddScoped<IExtService2, extService2>();
builder.Services.AddScoped<IExtService3, extService3>();
builder.Services.AddScoped<IServiceBase, ServiceAA>();
builder.Services.AddScoped<IServiceBase, ServiceAB>();
builder.Services.AddScoped<IServiceBase, ServiceBA>();
builder.Services.AddScoped<IServiceBase, ServiceBB>();
builder.Services.AddScoped<ServiceAA>();
builder.Services.AddScoped<ServiceAB>();
builder.Services.AddScoped<ServiceBA>();
builder.Services.AddScoped<ServiceBB>();
After that container can resolve the correct implementation for each service.
Another solution is to make a factory that can resolve a specific service by string or by another key and inject it instead of directly injecting service itself:
public enum ServiceType
{
ServiceAA,
ServiceAB,
ServiceBA,
ServiceBB
}
public interface IServiceFactory
{
IServiceBase CreateService(ServiceType serviceType);
}
public class ServiceFactory : IServiceFactory
{
private readonly IServiceProvider _serviceProvider;
public ServiceFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IServiceBase CreateService(ServiceType serviceType)
{
return serviceType switch
{
ServiceType.ServiceAA => _serviceProvider.GetService<ServiceAA>(),
ServiceType.ServiceAB => _serviceProvider.GetService<ServiceAB>(),
ServiceType.ServiceBA => _serviceProvider.GetService<ServiceBA>(),
ServiceType.ServiceBB => _serviceProvider.GetService<ServiceBB>(),
_ => throw new ArgumentException($"Invalid service type: {serviceType}")
};
}
}
After that you can register ServiceFactory
in DI-container:
builder.Services.AddScoped<IServiceFactory, ServiceFactory>();
And use it to resolve a concrete service:
public class SomeClass
{
private readonly IServiceFactory _serviceFactory;
public SomeClass(IServiceFactory serviceFactory)
{
_serviceFactory = serviceFactory;
}
public void SomeMethod()
{
IServiceBase serviceAA = _serviceFactory.CreateService(ServiceType.ServiceAA);
IServiceBase serviceAB = _serviceFactory.CreateService(ServiceType.ServiceAB);
}
}