Keep your ASP.NET Core startup project clean

Avoid unnecessary dependencies using IServiceCollection extensions

Benjamin Todts
Mar 13, 2018 · 2 min read

From the start, ASP.NET Core was designed to leverage dependency-injection, providing a built-in container to hold your services.

ConfigureServices

Consider we have an ASP.NET Core Web API project, with the following projects:

  • Foo.API
    This Web API project is our application’s Startup project. It contains the outer API-layer that will be exposed publicly.

The reason for this setup is, among others, inversion of control. Our domain works with interfaces, the actual implementation is inverted and defined in separate assemblies.

Inversion Of Control

Our API’s Startup class is as follows:

This ConfigureServices method is responsible for registering any services your app needs. Note that it has quite a few framework services registered by default.

Take a closer look at how we register our ApplicationDbContext: services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(…).

Remember that this method is part of our API project. While registering services, this project is fully aware that we are working with Entity Framework and using SqlServer. What’s more, for this to work, our API project actually needs to reference the Microsoft.EntityFrameworkCore assembly.

This breaks our carefully constructed inversion of control — why have a separate Foo.Infrastructure.EntityFramework project if our API has a dependency on Microsoft.EntityFrameworkCore anyway?

ServiceCollectionExtensions

Obviously, I wouldn’t be writing this blogpost if there was no elegant way around this.

Let’s inspect the ConfigureServices method signature: public void ConfigureServices(IServiceCollection services).
This services parameter is what provides access to ASP.NET Core’s underlying container. What’s better: we can easily write our own extensions for this IServiceCollection interface.

Let’s create such an extension in our Foo.Infrastructure.EntityFramework project:

Now let’s update our API’s startup class:

Great! Our API project no longer needs a dependency on Microsoft.EntityFrameworkCore, all it needs is a reference to our own Foo.Infrastructure.EntityFramework project. Notice that I purposefully named this method AddDataAccessServices as opposed to e.g. AddEntityFramework. Naming it the latter would feel like a leaky abstraction — if I were to switch to another ORM, say Nhibernate, I simply have to define a similar AddDataAccessServices method and create the reference to get this to work.

AddInfrastructureServices

I tend to create such extensions in every class library, even when said libraries don’t wrap a framework or third party dependency. In the example of our Foo API, I would also create an extension called AddInfrastructureServices to register the EmailSender.

That’s it! Using this elegant solution allows you to keep your container bootstrapping in your designated startup project, without violating pretty much every part of the SOLID acronym.


Since you’ve made it this far, I’m assuming you enjoyed reading this.
Thank you for taking your time, and don’t forget to 👏!