Generic Dependency Injection in .Net Core

Posted On: 11/12/2018 6:06:41 PM

Filed Under: Programming / .Net Core

I find dependency injection to be quite a useful pattern.  It also happens to be a pattern utilized in many of the code bases I've found myself using the most often.  Asp.Net Boilerplate and nopCommerce to name a couple, which use Castle Windsor and AutoFac respectively.

It wasn't until I started digging into .Net Core 2.1's native dependency injection that I realized, with a relatively small amount of work, you can write powerful applications that do a great deal of the heavy lifting for you!

The Problem Space

My goal is to get a better handle in .Net Core and Angular.  One way I enjoy familiarizing myself with new technology is to try and create a workable Content Management System.  I created a User, and Blog entity.  My IRepository interface and Repository object.  A UserService and a BlogService.  I've barely gotten off the ground and now have 4 separate objects to register in the IoC controller!

My startup.cs class (which of course, this stuff can and should be moved to a configuration object) is already starting to become a mess, and I've barely created any functionality!

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
    services.AddDbContext<ngCmsDbContext>(options => 
              options.UseSqlServer(Configuration.GetConnectionString("ngCmsConnectionString"), c => c.MigrationsAssembly("ngCmsBase.Web")));
    services.AddScoped(typeof(IRepository<Blog, long>), typeof(Repository<Blog, long>));
    services.AddScoped(typeof(IRepository<User, long>), typeof(Repository<User, long>));


It's becoming apparent this is going to get out of hand and I am going to miss something - soon.  But, there's an easy fix!

The scoping of the repository was really easy.  I just removed the two lines for:

services.AddScoped(typeof(IRepository<,>), typeof(Repository<,>));

Registering the services, however, took a little more work and the magic of reflection.

First, I created an empty interface, and made my Services implement it.  I then use reflection to track down all the objects that implement that interface, and simply register them on the fly, like so:

My interface is named: IngServiceBase 

var type = typeof(IngServiceBase);
var types = AppDomain.CurrentDomain.GetAssemblies()
                .SelectMany(s => s.GetTypes())
                .Where(p => type.IsAssignableFrom(p) && !p.IsInterface);
foreach(var t in types)

Now, no matter how many Entites I make, they will all immediately be able to make use of the Repository.  And no matter how many services I write, so long as they implement the IngServiceBase interface, they are ready to be injected!