Help me, to help you — helping Microsoft DI framework to find dependencies.

Cheranga Hatangala
Cheranga
Published in
3 min readJun 14, 2021

--

This post is about dependency injection using Microsoft dependency injection framework. I use it a lot in my daily work and in personal projects.

When compared to its alternatives such as AutoFac or Ninject one of the missing features (as of now) is to “discover” the dependencies and register in the framework. For example, in Autofac it can scan through the assemblies and register your classes as their implemented interfaces or as self.

So in this post let’s try to help Microsoft.DependencyInjection framework to find our dependencies and register them as we would like to do.

Context

Consider you have a class and an interface which it implements.

public interface IEmailService
{
Task<bool> SendAsync();
}

public class EmailService : IEmailService
{
public Task<bool> SendAsync()
{
throw new System.NotImplementedException();
}
}

If this service to be injected we need to “explicitly” write the code to register it in the dependency injection framework. For example if we would like to inject as a Transient dependency, we would need to write the code below.

// "services" is of type "IServiceCollection"
services.AddTransient<IEmailService, EmailService>();

Now imagine depending on your solution it can have 10, 15 or more different abstractions and you “will” need to write similar code such as above to register the dependency of each and everyone of them. What we are trying to do here is to help the Microsoft dependency injection framework to find our dependencies and to register them once and for all.

Approach

In here we are going to implement a custom attribute to provide information about how we would like to inject our dependency. Then we’ll implement an extension method so that it can be used with Microsoft.DependencyInjection . You can do this in your application (API, Web, Console, etc..) itself or create a class library project. In either case make sure you have installed Microsoft.Extensions.DependencyInjection nuget package.

Attributes

Simply, attributes are to provide additional information at runtime. Depending on how you define the attribute usage (class level, property level, etc..) you can apply the attribute in your code.

Extension Methods

Extension methods are there to add custom methods to existing types. They are great if you would like to extend the functionality of a given type customized to your requirement.

Let’s implement the attribute

The attribute can be used only in a Class level and its properties are,

  • LifeTime

This is of type ServiceLifeTime which is the type which is already defined in the Microsoft.Extensions.DependencyInjection framework.

  • InjectAs

This is an enum which will declare “how” you would like to inject your type. I have defined only two ways to inject the type and you can define any other type as you would like to be injected depending on your use case.

Let’s implement the extension method

  • Firstly it accepts a set of assemblies to scan for types.
  • Then for each type it checks whether it has the custom attribute InjectMeAttribute
  • Once it finds the types which have the InjectMeAttribute (candidate types) then it will check how it needs to be registered in the dependency injection framework.
  • We use the capabilities of Reflection and build the required types to be registered in the framework.
  • At the core of the dependency injection framework every dependency is defined as a ServiceDescriptor. So we use it to construct the source and target types and then register them in the dependency injection framework.

Using the attribute

Considering the same example provided above now let’s decorate the EmailService class with our attribute.

[InjectMe]
public class EmailService : IEmailService
{
public Task<bool> SendAsync()
{
throw new System.NotImplementedException();
}
}

Using the extension method

Let’s consider an ASP.NET Core Web API project to test this. In it’s ConfigureServices method in Startup.cs add the below method.

services.RegisterDependencies(typeof(Startup).Assembly);

You don’t need to define any dependencies “explicitly” as you did before for your classes anymore in the Startup class.

Whenever a type needs to be injected you just need to decorate it with the InjectMe attribute.

Thanks!

--

--