Monday, August 12, 2019

Dependency Injection in ASP.NET Core

Dependency Injection is the design pattern that help us to create application which loosely coupled. This means that object should only have those dependency that required during complete task. 
The main advantages of DI (Dependency Injection) is our application loosely coupled and has provide greater maintainability, testability and also re-usability. It is loosely coupled because dependency required by the class are injected from outer world rather than created them self directly win-in code.
There are three type of DI: Construction Injection, Setter Injection, Interface based Injection. The Construction Injection type of DI accept their dependency at constructor level it means that when create object of the class, their dependency pass through the constructor of the class. It provide the strong dependency contract between objects. 
The Setter Injection is also known as property injection. In this type of dependency injection, dependency pass through public property instead of constructor. 
It allows us to pass the dependencies when they required. It does not provide strong dependency contract between objects. The interface-based dependency injection can be achieved by creating the common interface and other classes are implements this interface to inject the dependency. 
In this type of DI, we can use either constructor injection or setter injection.
There is a built-in support of dependency injection in ASP.net Core. This supports is not limited to middleware, but also support in Controllers, views, and model as well. 
There are two type of service container provided by the ASP.net core: Framework Services and Application Services. 
The framework services are service that are provided by the ASP.net core such as ILoggerFactory etc. The application services are the custom services created base on our requirement.

Dependency injection into controllers

The dependency required by the ASP.net MVC controller requested explicitly via their constructors (Constructor injection type) and this dependency are available for the controller. 
Some of the dependency are injected to only the controller action as a parameter. ASP.net core has built-in support for constructor-based dependency. 
The dependency required by the controller are simply adding a service type to the controller in the constructor. 
The ASP.net core will identify the service type and try to resolve the type. It would be the good design if service defined using interfaces but it is not always true.
Example
In the following example, I have created HelloWorld service. This service has method called "SaysHello” that simply returns "Hello " string. I have also implement this service using interface.
  1. namespace DepedencyInjectionExample.Service
  2. {
  3. public interface IHelloWorldService
  4. {
  5. string SaysHello();
  6. }
  7.  
  8. public class HelloWorldService : IHelloWorldService
  9. {
  10. public string SaysHello()
  11. {
  12. return "Hello ";
  13. }
  14. }
  15. }
The next step is to add this service to the service container, so that when controller is requested for service, it is available to use. 
We can add the service to the service container in ConfigureServices method of startup class. There are three different life option available: 
Transient, Scoped, and Singleton. We will discuss this later part in the article.
  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. ….
  4. services.AddTransient<IHelloWorldService, HelloWorldService>();
  5. }
If we not register the service to the ASP.net core service container, it will throw the exception as following.
Now this “HelloWorld” service is available to use in the controller. We can inject this service as a dependency in constructor.
  1. using DepedencyInjectionExample.Models;
  2. using Microsoft.AspNetCore.Mvc;
  3. using DepedencyInjectionExample.Service;
  4.  
  5. namespace DepedencyInjectionExample.Controllers
  6. {
  7. public class HomeController : Controller
  8. {
  9. IHelloWorldService _helloWorldService;
  10. public HomeController(IHelloWorldService helloWorldService)
  11. {
  12. _helloWorldService = helloWorldService;
  13. }
  14. }
  15. }
Once the service has been configured correctly and injected in to the controller, it should display Hello message as expected.
The constructor dependency injection behavior resolved the service by either IServiceProvider or ActivatorUtilities.
The ActivatorUtilities allows to creation of the object without service registration in the DI. 
The model binder, tag helper and controller service are used ActivatorUtilities. The service required always public constructor. 
ASP.net core is only support the single constructor for the controller class which requesting the service. If we have more than one constructor, ASP.net core MVC raised the error.

Inject the dependency in controller action

Some time, we required dependency to the particular controller action method not to throughout controller. ASP.net core MVC allows us to inject the dependency to particular action using "FromServices" attribute. 
This attribute tell the ASP.net core framework that parameter should be retrieve from the service container.
  1. using DepedencyInjectionExample.Service;
  2. using Microsoft.AspNetCore.Mvc;
  3.  
  4. namespace DepedencyInjectionExample.Controllers
  5. {
  6. public class DemoController : Controller
  7. {
  8. public IActionResult Index([FromServices] IHelloWorldService helloWorldService)
  9. {
  10. ViewData["MyText"] = helloWorldService.SaysHello() + "Jignesh!";
  11. return View();
  12. }
  13. }
  14. }
The property injection is not supported by the ASP.net core but we call the service instance manually and called service methods.

Get the service instance manually

There is another way to get dependency services from the service container. In this method, service is not injected in controller constructor or in action method as parameter. 
Using method "GetService" of "HttpContext.RequestServices" property, we can get dependent services configured with Service container. This is also known as property injection. Following is the example.
  1. public IActionResult Index1()
  2. {
  3. var helloWorldService = (IHelloWorldService)this.HttpContext.RequestServices.GetService(typeof(IHelloWorldService));
  4. ViewData["MyText"] = helloWorldService.SaysHello() + "Jignesh Trivedi!";
  5. return View("index");
  6. }

Service Lifetime

ASP.net core allow us to specify the lifetime for registered services. The service instance gets disposed automatically based on specified life-time. 
So we do not care about the cleaning these dependency, it will take care by ASP.net core framework. There are three type of life-times.

Singleton

ASP.net core will create and share a single instance of the service through the application life. The service can be added as singleton using AddSingleton method of IServiceCollection. 
ASP.net core create service instance at the time of registration and subsequence request use this service instance. Here, we do not required to implement singleton design pattern and single instance maintained by the ASP.net core itself.
  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. ….
  4. services.AddSingleton<IHelloWorldService, HelloWorldService>();
  5. ….
  6. }

Transient

ASP.net core will create and share an instance of the service every time to the application when we ask for it. 
The service can be added as Transient using AddTransient method of IServiceCollection. This life-time can be used in stateless service. It is way to add lightweight service.
  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. ….
  4. services.AddTransient<IHelloWorldService, HelloWorldService>();
  5. ….
  6. }

Scoped

ASP.net core will create and share an instance of the service per request to the application. It means that single instance of service available per request. 
It will create a new instance in new request. The service can be added as scoped using AddScoped method of IServiceCollection.
We need to take care while, service registered via Scoped in middleware and inject the service in the Invoke or InvokeAsync methods. If we inject dependency via constructor, it behave like singleton object.
  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. ….
  4. services.AddScoped<IHelloWorldService, HelloWorldService>();
  5. ….
  6. }

Dependency injection into Views

ASP.net core can also able to inject the dependency to View. This is very useful to inject service related view such as localization. This method will bypass the controller call and fetch data directly from the service. 
We can inject the service dependency into the view using @inject directive. The syntax is as following to use this directive. Here we need to pass the service type that need to inject and service instance name that used to access the service method.
  1. @inject <type> <instance name>
Example
In the following example, I have used same service that created in preceding section and injected in the view using @inject directive and using the service instance, we can call the service method in to the view.
  1.  
  2. @{
  3. ViewData["Title"] = "DIToView";
  4. }
  5. @inject DepedencyInjectionExample.Service.IHelloWorldService helloWorldService
  6.  
  7. <h4>DI To View</h4>
  8.  
  9. <h5>
  10. @helloWorldService.SaysHello() Reader!!!
  11. </h5>
View injection can be used to populate the UI element such as dropdown. The common dropdown such city/state dropdown can be populate from the service. Rendering such things from the service is standard approach in ASp.net core MVC. 
Alternatively, we can use viewbag and Viewdata to populate dropdown. The directive @inject is also be used to override the injected service. 
For example, we are using Html helper service for the rendering the Html tags such as dropdown, textbox, etc. We can replace this service with our own service using @inject directive.

No comments:

Post a Comment

No String Argument Constructor/Factory Method to Deserialize From String Value

  In this short article, we will cover in-depth the   JsonMappingException: no String-argument constructor/factory method to deserialize fro...