Dependency Injection with .NET Core

Dependency injection is built-in with .NET Core. Microsoft.Extensions.DependencyInjection is a framework that is an essential part for ASP.NET Core, but can be used with other application types such as console applications, WPF, UWP, and also Xamarin. This article shows the core functionality of this new framework using a simple console application.

See the new version of this article – It’s all in the Host Class – Dependency Injection with .NET

Dependency Injection

Inversion of Control

Inversion of control is a design principle where – as the name says – the one who has control is inverted. Instead of a method (typically one within a library) that defines the complete functionality on its own, the caller can supply code. This code in turn is invoked by the called method.

Using .NET, inversion of control can be implemented by using delegates or interfaces.

The following sample code shows inversion of control in action using constructor dependency injection. With the constructor of the HelloController class, an object implementing IGreetingService is injected. This object is remembered in a readonly variable, and used within the Action method:

public class HelloController
{
  private readonly IGreetingService _greetingService;
  public HelloController(IGreetingService greetingService)
  {
    _greetingService = greetingService ?? throw new ArgumentNullException(nameof(greetingService));
  }

  public string Action(string name) =>
    _greetingService.Greeting(name);
}

The interface IGreetingService itself just defines the Greeting method. This interface is all what needs to be known by the HelloController class, this class doesn’t need to know anything about the concrete implementation of this interface.

public interface IGreetingService
{
  string Greeting(string name);
}

Inversion of control is also known by the name “Hollywood principle” – don’t call us, we’ll call you.

Injecting the Service

The caller using the HelloController class has the responsibility to inject a concrete implementation for the interface IGreetingService. The implementation that will be used is defined by the type GreetingService:

public class GreetingService : IGreetingService
{
  public string Greeting(string name)
  {
    if (name == null) throw new ArgumentNullException(nameof(name));
    return $"Hello, {name}";
  }
}

The class GreetingService implements the required interface and thus can be used to pass it to the constructor of the HelloController. This is also known by the principle dependency injection, a dependency is injected. The sample code injects the service object into the constructor of the HelloController:

IGreetingService service = new GreetingService();
var controller = new HelloController(service);
string greeting = controller.Action("Matthias");
WriteLine(greeting);

With dependency injection, a dependency such as a type implementing the IGreetingService is injected.

Microsoft.Extensions.DependencyInjection

Instead of injecting the dependencies on your own, you can also use a dependency injection container.
.NET Core brings its own dependency injection container – Microsoft.Extensions.DependencyInjection. This container is used from ASP.NET Core, and can also be used with UWP and applications using the full .NET Framework, such as WPF.

Using a Container

To use this framework, you just need to add the NuGet package Microsoft.Extensions.DependencyInjection. First, the services that should be injected are collected. The ServiceCollection class keeps a list of the services, and returns a IServiceProvider object that can be used to access the registered services. Several extension methods can be used to register services: AddSingleton, AddTransient, and AddScoped.

private static void RegisterServices()
{
  var services = new ServiceCollection();
  services.AddSingleton();
  services.AddTransient();
  Container = services.BuildServiceProvider();
}

public static IServiceProvider Container { get; private set; }

Using Singleton Objects

The dependency for IGreetingService is registered as a singleton using AddSingleton. This gives the information to the container to always return the same instance when it is asked for. The dependency injection container keeps a ServiceDescriptor for each registered object which holds information about the implementation instance, the service type, the implementation type, and the service lifetime. You can also pass a factory method that is used to instantiate the object. The ServiceLifetime is an enumeration that can be one of the values Singleton, Scoped, and Transient.

Using Transient Objects

The HelloController is registered using AddTransient. With AddTransient, always a new object is created when it is retrieved from the container.

Scoped Objects

There’s also something between singleton and transient: scoped. With scoped, always the same object is retrieved within the same scope, but from another scope a different object. What’s a scope? With ASP.NET Core applications, a scope is a single Web request. If the same type is retrieved from within the same request (e.g. from different controllers, services, or views), always the same object is returned. However, different Web requests get other instances.

Service and Implementation Types

My sample code used generic methods to pass the service and implementation types. Invoking the method AddSingleton, the implementation type GreetingService is specified to be instantiated for the service type IGreetingService. When the type IGreetingService is requested, a GreetingService instance is returned. You can also specify just the implementation type, like shown with the AddTransient method. This way, the HelloController needs to be requested, and a HelloController is returned.

The methods AddSingleton, AddTransient, and AddScoped are not really defined by the ServiceCollection class. The ServiceCollection class just defines an indexer to get and set registered services, to insert and remove services, and to check if a service is registered. AddSingleton, AddTransient, and AddScoped are implemented as extension methods. A lot more extension methods are available, e.g. extension methods that allow passing an implementation factory.

Retrieving Objects from the Container

Now is the time to get objects from the container. This can be done easily using the GetService method of the IServiceProvider. Remember, the constructor of the HelloController requires an object implementing IGreetingService. There’s no need to deal with this, the dependency injection container knows how this type can be resolved – by returning a single instance of the type GreetingService.

  var controller = Container.GetService();
  string greeting = controller.Action("Stephanie");
  WriteLine(greeting);

The Managed Extensibility Framework (MEF) can be used for dependency injection as well. MEF supports to inject dependencies using attributes or programmatically.

Why?

What’s the advantage of using dependency injection? One advantage is for easier unit testing. For example, creating unit tests for the HelloController you wouldn’t want to test dependencies such as accessing the database or calling services. With dependency injection, it’s easy to mock the dependencies and create separate types that are used for the test.
Another advantage is to create independence from specific technologies. For example, you can create a view-model using .NET Core libraries, and concrete implementations of services using WPF, UWP, and Xamarin. The concrete implementation comes from the caller which is the executable, while the contracts and view-models can be implemented technology agnostic.

More Information

A larger sample using Microsoft.Extensions.DependencyInjection is shown in my blog with Using .NET Core Libraries from UWP. More samples are in the book Professional C# 7 and .NET Core 2.0. Chapter 20 is fully dedicated to dependency injection. Chapter 34, Patterns with XAML Apps shows the Microsoft.Extensions.DependencyInjection both with Xamarin and UWP. Chapters 30 to 32 use this framework with ASP.NET Core. Of course you’ll also learn more about this framework in my workshops ASP.NET Core, and MVVM.

Have fun with programming and learning,
Christian

Get the source code for this article at GitHub

20 thoughts on “Dependency Injection with .NET Core

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.