Keep your ASP.NET Core startup project clean
Avoid unnecessary dependencies using IServiceCollection extensions
From the start, ASP.NET Core was designed to leverage dependency-injection, providing a built-in container to hold your services.
Consider we have an ASP.NET Core Web API project, with the following projects:
Foo.APIThis Web API project is our application’s Startup project. It contains the outer API-layer that will be exposed publicly.
Foo.DomainThis project is a class library, holding objects that belong to our domain. This includes any models as well as interfaces for e.g. services, repositories,…
Foo.InfrastructureAnother class library, containing the infrastructure-layer of our project.
Amongst others, this layer defines implementations for interfaces defined in our domain (not necessarily all of them).
Foo.Infrastructure.EntityFrameworkThis project represents an extensions of the Infrastructure layer, containing Entity Framework-specific code.
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
Startup class is as follows:
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
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
This breaks our carefully constructed inversion of control — why have a separate
Foo.Infrastructure.EntityFramework project if our API has a dependency on
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).
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
Let’s create such an extension in our
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.
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
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 👏!