Dependency Injection with Generic Interface and Inheritance

48 Views Asked by At

I recently thought about a case where i can build the abstractions of news importer into the .NET DI Contianer.

This is my example Case.

This class represents a news source.

public abstract class Source {
   public Guid Id {get; set;}
}

This concrete Source is a Rss Feed.

public class RssFeed: Source {
   public string Url {get; set;}
}

Now i have a generic interface for an importer of some source.

public interface ISourceImporter<TSource> where TSource:  Source {
Task IEnumerable<Post> ImportAsync(TSource source)
}

And a concrete Implementation for the RssFeed Sorce

public class RssFeedImporter: ISourceImporter<RssFeed> {
}

Register the Service into the DI Container

services.AddSingleton<ISourceImporter<RssFeed>, RssFeedImporter>()

So the Main problem ooccurs at the usage of the service throug the ServiceProvider. I my Command i want to abstract the concrete type of my source like this.

public Task ImportCommand(Source source) {
   ISourceImporter<Source> importer = (ISourceImport<Source>)serviceProvider
           .GetRequiredService(typeof(ISourceImporter<>).MakeGenericType(source.GetType()))

   var posts = await importer.ImportAsync(source);
}

Is there a way to build that abstraction with the .NET Dependency Container? Maybe is my structure not suitable for this? I read an article about open generics but this gives me a circular dependency when building the DI Container.

Thanks for your help.

1

There are 1 best solutions below

0
Steven On

If the solution can't be solved by turning ImportCommand(Source) into an ImportCommand<TSource>(TSource source) where TSource : Source, you will have resort to using Reflection.

The easiest way (least amount of code) is by making use of the dynamic keyword:

public async Task ImportCommand(Source source)
{
   Type importerType = typeof(ISourceImporter<>).MakeGenericType(source.GetType());
   dynamic importer = serviceProvider.GetRequiredService(importerType);
   await importer.ImportAsync(source);
)

But note the following:

  • The call to ImportAsync will fail when you refactor the interface. That, however, is easily solved by writing a unit test for ImportCommand.
  • The call to ImportAsync will fail when the ISourceImporter<T> implementation is internal, even if the interface is public.