I have the following minimal, reproducible example.
public class BaseEntity{}
public class BaseChildClass: BaseEntity{}
public class ChildA : BaseChildClass {}
public class ChildB : BaseChildClass {}
public interface IRepository<TEntity> where TEntity : BaseEntity{}
public class DataRepository<TEntity> : IRepository<TEntity>
where TEntity : BaseEntity{}
public class Program
{
public static void Main()
{
var services = new ServiceCollection();
if (ChildAFeature) // loaded from config
{
services.AddScoped<IRepository<BaseChildClass>, DataRepository<ChildA>>();
}
else
{
services.AddScoped<IRepository<BaseChildClass>, DataRepository<ChildB>>();
}
var provider = services.BuildServiceProvider();
var service = provider.GetService<BaseChildClass>();
Console.WriteLine(service.GetType());
}
}
The problem is when trying to inject service like this:
services.AddScoped<IRepository<BaseChildClass>, DataRepository<ChildA>>();
Compile error shows
there is no implicit convertion from DataRepository<ChildA> to IRepository<BaseChildClass>
I need to inject either ChildA
or ChildB
to the repository depending on a feature flag. So basically, some times I need childA repo and sometimes childB repo based on a condition.
CodePudding user response:
You are doing the service registrations in the wrong way.
Updated your dot-net fiddle Fiddle Link.
Here's the way to register generic dependencies.
public static void Main()
{
var services = new ServiceCollection();
services.AddScoped(typeof(IRepository<>), typeof(DataRepository<>));
services.AddScoped<BaseChildClass, ChildA>();
var provider = services.BuildServiceProvider();
var repo = provider.GetService<IRepository<ChildA>>();
Console.WriteLine(repo.GetType());
var service = provider.GetService<BaseChildClass>();
Console.WriteLine(service.GetType());
}
CodePudding user response:
Generic parameters don't really work with inheritance.
You can however declare the T in the interface as <out T>
, then your use case should work. This does however force you to only use T as a return value, not as an input value. You can take a look at how IEnumerable<T>
is declared, it does this too.