I think it’s safe to say that there’s a lot of hype around .NET Core nowadays because of it being cross-platform, open source, etc, which isn’t really something we’ve been used to from Microsoft technologies over the years. So as someone who works in a predominantly .NET dominated environment, I wanted to dedicate some time playing around with it, building a simple API & just investigating how different it is to the regular .NET Framework.
One of the more intriguing perks of .NET Core that sparked my interest was its built in Dependency Injection which I’d assume is a concept most software developers are somewhat acquainted with. If you don’t know what Dependency Injection is, it is a way of passing a classes external dependencies without having to instantiate the dependency every single time an instance of the base class is used. It has many useful uses, but one of my favourite is probably the fact that it makes mocking & testing classes a lot easier as well as helps overall with decoupling.
Here’s a quick example scenario, which should hopefully explain this:
- Imagine we have a
UserServiceclass, containing logic related to Users and a
UserRepositoryclass that talks to a database.
- A level above that, we also have a
UserControllerclass, which in an API project for example could contain methods that are mapped to
POSTroutes which could then allow your Angular, React or Vue front-end to call them or pass data to them to be executed.
- If a user is attempting to log in via our API, they will first hit a route defined within our
UserControllerwhich would then require the
UserServiceto work its magic and talk to a database somewhere via the
UserRepository, compare password hashes & eventually return whether or not the user has indeed successfully managed to login successfully or not.
- Given that our
UserServicetalks to a separate class which is the
UserRepository, you’d probably normally do something like this simplified example:
- So let’s say we now decide that we want to write some unit tests around our
UserServicebut because of the nature of tests, we don’t actually want our tests to force the
UserRepositoryto talk to or play around with the database in any way shape or form. For logging in, this scenario isn’t too bad. But imagine you’re testing registering a new user… what do you want to do, create a new user object in the database every time you run your unit tests? This would cause chaos!
- A suitable solution would be to obviously mock out our
UserRepository, so that when we Unit Test the
UserService, we use a mocked out
UserRepositorywhere we can define what it should return when we pretend to call the
GetUser()method and allow us to test our
UserServicein isolation. But we don’t really want to mock out the
UserServicecode because then we’re modifying our production code… this is where Dependency Injection becomes your best friend.
- We have now applied Dependency Injection to our
UserServiceclass. Instead of instantiating our
UserRepositorywithin the constructor of our
UserService, we’ve declared an interface of it at the top of the class and assigned it to the dependency passed in via the constructor parameters and due to the magic of dependency injection, regardless of whether you’re using a framework like Spring, the built in .NET Core dependency injection or something like Castle Windsor to handle it for you, every time the
UserServiceis being used, it will always have access to a usable instance of the
UserRepositoryand the functions inside.
- Within our tests, we can use a mocking framework to mock out the
UserRepositorybased on an interface of the
IUserRepository. This now means that our mocking framework within our unit tests will now be able to mock out an
IUserRepositoryand pass it to our
UserService’s constructor when testing it. Allowing the
UserServiceto run and thus be tested in isolation without actually hitting the database through the real
- Now, not only is our production code looking much better with reduced coupling, but we can also test our code in a much easier, more isolated fashion.
- The above test doesn’t really test anything as it is just to showcase how the use of Dependency Injection makes life easier for us in terms of testing. In a real scenario, the
UserServicewould more than likely contain more logic within the
Login()method we are testing against.
Back on topic, normally we tend to use Castle Windsor to handle Dependency Injection at work because of the perks that come with it being a full fledged IoC container. But what intrigued me about .NET Core’s built in Dependency Injection was the claim that it was packaged with the framework. Whilst in older versions you would have to manually set up and configure the Dependency Injection yourself - which nowadays just means wasting time with the number of packages out there that do it for you - it apparently could be done in .NET Core with a few lines the same way something like Castle Windsor allows you to do once set up.
So, to do this all you have to do is simply navigate to your .NET Core project’s
Startup.cs file and add the following under the
ConfigureServices(IServiceCollection services) method:
AddTransient() method creates a brand new instance of the
UserRepository() each and every single time it is requested, even if requested within the same scope, these are great for stateless APIs. This means every time any kind of Service within a project uses a
UserRepository, it will have a brand new Repository injected to use.
AddScoped() method creates a new instance of the
DogRepository each time it is requested within different scopes. This means every Service within a project that uses a
DogRepository, will have its own single
DogRepository to keep re-using.
AddSingleton() method creates a new instance of the
CatRepository once and shares the same instance across all different scopes within the application. This means every Service within a project that uses a
CatRepository, will be injected with and use the same single
AddInstance() method passes a specific instance of an
IRabbitRepository type object as parameter, meaning each and every time it is requested regardless of scope, that specific
RabbitService instance that was manually instantiated in the code above, will always be used and shared rather than let the Dependency Injection itself instantiate the object.
And now… breathe! That’s it. You have now set up the built in Dependency Injection within .NET Core in your project! Every time you require a dependency, simply register it however you like within your Startup.cs file and then just pop the interface in your constructor as shown above!