You are currently viewing Demo Console App For Understanding Dependency Injection in .NET

Demo Console App For Understanding Dependency Injection in .NET

Generally, if we create a .NET core web application using Visual Studio or dotnet CLI, the template already has Startup class. This class contains all basic setup, ready to use for using many different services.

So, in this article, let’s try to setup a demo console application to use dependency injection. That might help us understand the internals of how does it work.

Console App

Create a console application (let’s say DIConsoleDemo) using dotnet CLI or using the visual studio. Then add reference of the Microsoft.Extensions.Hosting NuGet package to the project.

Set of Interfaces

We are going to create an interface IGetCreatedTime, which has a method GetCreatedTime. The idea is to have a class which implements this interface (indirectly, wait for next step) and returns a DateTime object, representing the time at which the current object was created.

To better understand the concept of singleton, scoped and transient service lifetimes discussed in previous post, let’s create three more interfaces:

  • ISingletonGetCreatedTime
  • IScopedGetCreatedTime
  • ITransientGetCreatedTime

Each one of the above three interfaces should implement the original interface IGetCreatedTime and they will not contain any extra methods. All these 4 interfaces are shown in the below code snippet.

Implementation

The implementation class is simple. It will implement three interfaces corresponding to three lifetimes. The class will capture the current date time in the constructor and will hold it in the readonly field. The GetCreatedTime method from the interface should return the readonly field.

The concrete class is shown in the code snippet below:

Invoker

This is a class where the constructor expects the three objects:

  • first implementing ISingletonGetCreatedTime,
  • second implementing IScopedGetCreatedTime,
  • third one implementing ITransientGetCreatedTime

Then there is a method invoke, which just invokes GetCreatedTime method on each of the objects and prints the output as shown in below snippet.

Main

For dependency injection, there are three important interfaces:

  • IHost, this is the class which exposes the property of IServiceProvider
  • IServiceCollection, the collection where we can register details about service interface, its concrete implementation and the lifetime of the service, typically while the application is bootstrapping.
  • IServiceProvider, which acts as container responsible for creating/maintaining/disposing the services registered using IServiceCollection

This is where the Nuget package is going to help. The CreateDefaultBuilder method creates an IHostBuilder instance with the default binder settings. ConfigureServices method provides the instance of IServiceCollection (services parameter) which we have used to register three interfaces. So each of the interface creates object of same class, but the lifetime of the object created is different.

As this is console application, it is not like web application and that’s why there is always going to be a single scope. But to simulate multiple scopes we have added a call to IServiceProvider.CreateScope() which creates a new scope.

For every scope, we invoke the invoker object’s method two times and the creation time of every object is printed to help us understand how the objects are getting used.

Run and Verify

Run the application and you should be able to see the output as shown below:

Dependency Injection in .NET: Demo of service lifetimes

So, we have successfully demonstrated how the service lifetime works. I hope you find this demo useful. Let me know your thoughts.

Leave a ReplyCancel reply

This Post Has 5 Comments

  1. Rob Bastida
    Rob Bastida

    Thanks for the straightforward explanation. How would we set up some unit tests for this application?

    1. Manoj Choudhari
      Manoj Choudhari

      Glad to know it helped you ! My question is – what exactly do you want to test ?

      If you want to test the business functionality (equivalent to GetCreatedTimeInvoker in this post), then you can just instantiate it using new operator inside the test.

      If you want to test the DI container setup itself, then you must refer the same method which initializes the container to setup a container in the unit test. Then resolve the services explicitly to ensure that they are getting resolved as expedted.

      Hope this helps !

      1. Rob Bastida
        Rob Bastida

        Hi Manoj, I’m wanting to test the business functionality but will need to load config – eg db connections. So just calling new on the business object wont work as it would have no config info.

      2. Manoj Choudhari
        Manoj Choudhari

        If I understand correctly, you want to use your configurations to connect to database. If you need to test functionality which includes database connectivity, I would suggest to better go for API / Integration tests. Integration tests will not have any mocks and you will need to use the same startup class from your host application in the tests. If you just need to test the functionality, without needing to connect to actual database, then you can write unit tests. In case of unit tests, you will have to mock the dependencies while instantiating the business implementation. Hope this helps.