# ASP .NET Core DI: Dependency on single implementation of multiple registrations of service type

Problem

I have a DI scenario where I have multiple classes that implement a common interface (IModelService).

I'm using a generic framework class (PredictionEnginePool<,>) that has options injected (PredictionEnginePoolOptions<>). This has a property (ModelLoader) that must be set using AddOptions in Startup.ConfigureServices where I set it to an instance of CustomModelLoader obtained from the service provider (from the AddSingleton implementation factory) during options configuration. CustomModelLoader depends on a single IModelService for its lifetime, so it needn't be injected with IEnumerable<IModelService>.

Research

I have researched various patterns to achieve my aim which is to have CustomModelLoader have access to a single registered IModelService object while being assigned to the ModelLoader property of the options class PredictionEnginePoolOptions<,> (which is then injected into PredictionEnginePool<,>). But most of the solutions I've found use a service locator anti-pattern or break SRP and/or OCP; for example, most of the solutions offered on this SO question. And many people suggest that the built-in DI framework of .NET Core is too simple to cater for complex scenarios and to use something like Autofac or StructureMap.

However, I refuse to believe that Microsoft would make such a scenario difficult or impossible (as it's not that complicated) in a new modern framework as .NET Core which builds on almost two decades of experience from .NET Framework. I've been pretty impressed with .NET Core so far in terms of complying with and enabling SOLID and other good coding principles.

Own Attempt So Far

The code below shows my attempt to resolve this based on various related questions on SO. Basically, to provide CustomModelLoader with a single IModelService object, I inject options into it (CustomModelLoaderOptions). This has a IModelService property on it. To set this, I have another call to AddOptions which sets the property (ModelService) by getting the single IModelService object that returns true from CanTrain (a strategy design pattern). I use GetServices<IModelService> to resolve the IModelService objects. CanTrain is implemented in each concrete type (RegressionModelService and TimeSeriesModelService) to state whether it can work with the type specified at startup for each generic version of PredictionEnginePool (RegressionPrediction and TimeSeriesPrediction which are just simple POCOs).

These are the interfaces and classes:

interface IModelService
{
bool CanTrain(Type type);
}

class RegressionModelService : IModelService
{
public bool CanTrain(Type type) => type == typeof(RegressionPrediction);
}

class TimeSeriesModelService : IModelService
{
public bool CanTrain(Type type) => type == typeof(TimeSeriesPrediction);
}

{
public IModelService ModelService { get; set; }
}

{
}


My only real restriction is PredictionEnginePool<,> and PredictionEnginePoolOptions<,>. These are framework classes that cannot change and must be registered as per below. The following code is in Startup.ConfigureServices:

services.AddScoped<IModelService, RegressionModelService>();

{
{
{
{
options2.ModelService = sp.GetServices<IModelService>().Single(s => s.CanTrain(typeof(T)));
});


However, I am not entirely happy with my solution because it still requires me to call GetService on the service provider to get IModelService and then assign it to a property on CustomModelLoaderOptions which is then injected into CustomModelLoader. I don't mind doing this for CustomModelLoader itself and assigning it to a property on PredictionEnginePoolOptions<,> which is then injected into PredictionEnginePool<,> as this is the documented way (per the sample applications) to do it for this class, but I feel I'm just copying this methodology to work around another design problem that could be solved without having to call GetService, and somehow let the DI framework take care of it. Is this possible, perhaps with a different code design pattern?