Implementing Dependency Injection in ASP.NET Core

Dependency Injection is the heart of clean architecture for an ASP.NET Core application. It is supported out of the box when you create an ASP.NET Core application with the Microsoft.Extensions.DependencyInjection module. In the start up file, you can add the mapping between interface and actual implementation. IServiceCollection has methods to add services to the container with Lifetime. When the app starts up, services are added to the container. When a class is instantiated, concrete classes are to be injected.

This is really all you need to do to support DI in the ASP.NET core app.

The problem with this approach is that we need to keep adding dependencies as we add more classes and interfaces. What if we have hundreds of dependencies and we need to keep track? This solution becomes unmanageable quickly. It is also annoying to update a dependency in another file whenever we create a new class.

New Solutions

So, here is my solution that I borrowed from Sitecore’s Habitat example. I have written a blog about DI in Sitecore where I mentions this example. As it is using the out-of-the-box Microsoft Dependency Injection module, it can be used for ASP.NET Core applications.

The idea is very simple. We extend IServiceCollection to have methods to read class attributes with service type and lifetime information by reflection. Then, dependencies get registered when the app starts up. In this way, we can manage dependencies in the same class file by adding attributes instead of keeping in the separate files.

See what I mean below.

We can add attributes with service type and lifetime. See my blog on DI lifetime here for further information.

In the startup, we can add a new method with the wildcard keyword for the dll. With the extended method, it will look for the service attribute in all the classes including the word passed as an argument. Look how manageable the Startup.cs file becomes!

Implementing IServiceCollection Extensions

Let’s dive deep into the actual implementation. As I mentioned above, I adapted this solution from Sitecore’s Habitat example. There are a bit of changes. If you copy & paste this solution, it will work for ASP.NET Core apps. One of the biggest difference is that we do not need to register controllers explicitly as we can use AddMvc() to register controllers.

If your solution has only one project, you can create a folder called Extensions and add them in there. If you are using an Onion architecture like this example, I would put it in the Application Core as it needs to be referenced by other projects. It is however an anti-pattern as this extension is not really a business logic.

So, once you have the Extension folder, you need to create two files. One is for the attribute model and the other is the actual extensions.

ServiceAttribute.cs

SearviceCollectionExtenstions.cs

Once  you add these methods, you can start using attributes to register dependencies in your ASP.NET Core application.

Sweet!

Wait, there is more… What about writing unit tests on this extension method?

Unit Tests

First, we create mock classes and interfaces in the test project.

MockClassForExtensionsTests.cs

Then, we can instantiate ServiceCollection and call the newly created extension method, AddClassesWithServiceAttribute and target dll in the unit test project. This will read attributes from the mock classes created above from the dll.

ServiceCollectionExtensionsTest.cs

This is it for now. It is your turn to try this out. Let us know how you go!

Front-End
TypeScript: type aliases to check type equality

This post is to analyse how the type equality check by using type aliases proposed by Matt Pocock in his twitter post. These type aliases allow us to elegantly express type equality checks in unit tests. All we need to do is to pass the output and expected types in …

Front-End
Fixing it.only type error in Jest

If you are getting a type error with it.only in Jest, it could be due to incorrect TypeScript typings or incompatible versions of Jest and TypeScript. To resolve this issue, you can try the following steps: Make sure you have the latest versions of Jest and its TypeScript typings installed …

Front-End
yup conditional validation example

Here’s an example of a Yup validation logic where the first input field is optional but, if filled, it must contain only alphabetic characters, and the second input field is required: import * as Yup from “yup”; const validationSchema = Yup.object().shape({ firstField: Yup.string().matches(/^[A-Za-z]*$/, { message: “First field must contain only …