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:
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.
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
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?
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.
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 👏!
Fill in the form below and we’ll get back to you as soon as possible.
Thanks for getting in touch!Oops. You seem to have written your full name in invisible ink. Please enter it so we can read it. Oops. You seem to have written your company in invisible ink. Please enter it so we can read it. It seems your e-mail doesn’t exist. Please enter a real one so we can contact you. Oops. You seem to have written your telephone in invisible ink. Please enter it so we can read it. Sorry, we could not send the enquiry.