ASP. NET Core dependency injection, asp. netcore

Source: Internet
Author: User

ASP. NET Core dependency injection, asp. netcore
1. What is dependency Injection? Why use it? It is especially easy for beginners to confuse concepts such as IOC (Iversion of Control) and DI. 1.1 Dependency a dependency is generated when one class requires another class to complete the work. For example, we need to complete user-related registration and login in the AccountController controller. The logon is completed by EF and Idnetity, So we encapsulate an EFLoginService. Here, AccountController has an ILoginService dependency. Here is a design principle: It depends on abstraction rather than concrete implementation. Therefore, we defined an interface for EFLoginService to abstract the behavior of LoginService. 1.2 What injection represents is an IOC (the idea of inversion control ). Before turning it back, let's take a look at the forward direction. AccountController instantiate the required Dependencies by itself.

private ILoginService<ApplicationUser> _loginService;public AccountController(){  _loginService = new EFLoginService()} 

The Master said, this is not good. You should not create it by yourself, but by your caller. So you can use the constructor to send the two dependencies to you.

 public  AccountController(ILoginService<ApplicationUser> loginService){  _loginService = loginService;}

The creation of dependencies is lost to others, and they are only responsible for use. The process of throwing others to your dependencies is understood as injection.

1.3 why is it reversed? In order to minimize the possible problems caused by code changes during business changes. For example, we want to change the login verification from EF to read from Redis, So we add a RedisLoginService. At this time, we only need to change it in the original injection.
Var controller = new AccountController (new EFLoginService (); controller. login (userName, password); // use Redis to replace the original EF logon var controller = new AccountController (new RedisLoginService (); controller. login (userName, password );
1.4 What is the container above when we use AccountController, we create an ILoggingServce instance through code. Imagine if there are 100 such places in a system, do we want to do this in 100 places? The control is reversed, and the dependency creation is also handed over to the outside. The problem is that there are too many dependencies. We need to unify all the dependencies in a local management system, and the container is born. The container is responsible for two tasks:
  • Relationship between services and Instances
  • Obtain and manage instances (create and destroy instances)
Ii. After clarifying the key concepts of DI and Ioc before registering a. NET Core DI 2.1 instance, let's take a look at the applications of. NET Core DI in the console. In. NET Core, DI has two Core components: IServiceCollection and IServiceProvider.
  • IServiceCollection is responsible for registration
  • IServiceProvider is responsible for providing instances
The default ServiceCollection (under the Microsoft. Extensions. DependencyInjection namespace) has three methods:
  var serviceCollection = new ServiceCollection()  .AddTransient<ILoginService, EFLoginService>()  .AddSingleton<ILoginService, EFLoginService>()  .AddScoped<ILoginService, EFLoginService>();
All three methods register our instances, but the instance life cycle is different. When will the lifecycle be discussed in the next section. By default, ServiceCollection provides a List of ServiceDescriptor.
public interface IServiceCollection : IList<ServiceDescriptor>{}

The AddTransient, AddSignletone, and Scoped methods above are extension methods of IServiceCollection, and ServiceDescriptor is added to this List.

private static IServiceCollection Add(  IServiceCollection collection,  Type serviceType,  Type implementationType,  ServiceLifetime lifetime){  var descriptor =   new ServiceDescriptor(serviceType, implementationType, lifetime);  collection.Add(descriptor);  return collection;}
2.2 The life cycle of an instance is shown in the preceding figure. NET Core DI provides three instance life cycles for us:
  • Transient: each time GetService creates a new instance
  • Scoped: Initialize only one instance in the same Scope. It can be understood that (only one instance is created at each request level, and the same http request will be in one scope)
  • Singleton: only one instance is created within the entire application lifecycle.
Corresponds to the three enumeration values of Microsoft. Extensions. DependencyInjection. ServiceLifetime
public enum ServiceLifetime{  Singleton,  Scoped,  Transient}
In order to better understand the concept of this lifecycle, we will conduct a test: Define a basic IOperation with an OperationId attribute. The same is true for IOperationSingleton, which is just another interface.
public interface IOperation{        Guid OperationId { get; }}public interface IOperationSingleton : IOperation { }public interface IOperationTransient : IOperation{}public interface IOperationScoped : IOperation{}

Our Operation implementation is very simple. You can input a Guid In the constructor to assign values. If not, you can create a New Guid.

public class Operation :   IOperationSingleton,  IOperationTransient,  IOperationScoped{    private Guid _guid;    public Operation() {        _guid = Guid.NewGuid();    }    public Operation(Guid guid)    {        _guid = guid;    }    public Guid OperationId => _guid;}

In the program, we can call the ServiceProvider's GetService method multiple times to obtain the same instance.

Var services = new ServiceCollection (); // constructs services by default. addSingleton <IOperationSingleton, Operation> (); // you can specify a Guid for null services. addSingleton <IOperationSingleton> (new Operation (Guid. empty); // custom input of a New Guidservices. addSingleton <IOperationSingleton> (new Operation (Guid. newGuid (); var provider = services. buildServiceProvider (); // output Guidvar singletone1 = provider. getService <IOperationSing Leton> (); Console. writeLine ($ "signletone1: {singletone1.OperationId}"); // output the Guidvar singletone2 of singletone2 = provider. getService <IOperationSingleton> (); Console. writeLine ($ "signletone2: {singletone2.OperationId}"); Console. writeLine ($ "singletone1 = singletone2? : {Singletone1 = singletone2 }");

We registered IOperationSingleton three times and finally obtained it twice. You should note that we have always obtained the last registered instance that gave a Guid, and the previous one will be overwritten.

2.3 Tranisent of instance Life Cycle

The IOperationTransient we obtained this time is two different instances.

var services = new ServiceCollection();services.AddTransient<IOperationTransient, Operation>();    var provider = services.BuildServiceProvider();var transient1 = provider.GetService<IOperationTransient>();Console.WriteLine($"transient1: {transient1.OperationId}");var transient2 = provider.GetService<IOperationTransient>();Console.WriteLine($"transient2: {transient2.OperationId}");Console.WriteLine($"transient1 == transient2 ? :   { transient1 == transient2 }");
2.4 Scoped. NET Core person IServiceProvider for instance lifecycle provides createlist to generate a new ServiceProvider range. The instances marked by Scope under this range will only be the same instance. In other words, an object registered with Scope is equivalent to a singleton under the Scope of the same ServiceProvider. Similarly, we first register the IOperationScoped, IOperationTransient, and IOperationSingletone instances respectively, and use the corresponding Scoped, Transient, and Singleton lifecycles.
 var services = new ServiceCollection().AddScoped<IOperationScoped, Operation>().AddTransient<IOperationTransient, Operation>().AddSingleton<IOperationSingleton, Operation>();

Next we will use ServiceProvider. createlist to create a Scope

var provider = services.BuildServiceProvider();using (var scope1 = provider.CreateScope()){    var p = scope1.ServiceProvider;    var scopeobj1 = p.GetService<IOperationScoped>();    var transient1 = p.GetService<IOperationTransient>();    var singleton1 = p.GetService<IOperationSingleton>();    var scopeobj2 = p.GetService<IOperationScoped>();    var transient2 = p.GetService<IOperationTransient>();    var singleton2 = p.GetService<IOperationSingleton>();    Console.WriteLine(        $"scope1: { scopeobj1.OperationId }," +        $"transient1: {transient1.OperationId}, " +        $"singleton1: {singleton1.OperationId}");    Console.WriteLine($"scope2: { scopeobj2.OperationId }, " +        $"transient2: {transient2.OperationId}, " +        $"singleton2: {singleton2.OperationId}");}

Next

scope1: 200d1e63-d024-4cd3-88c9-35fdf5c00956, transient1: fb35f570-713e-43fc-854c-972eed2fae52, singleton1: da6cf60f-670a-4a86-8fd6-01b635f74225scope2: 200d1e63-d024-4cd3-88c9-35fdf5c00956, transient2: 2766a1ee-766f-4116-8a48-3e569de54259, singleton2: da6cf60f-670a-4a86-8fd6-01b635f74225

If you create a new Scope to run,

scope1: 29f127a7-baf5-4ab0-b264-fcced11d0729, transient1: 035d8bfc-c516-44a7-94a5-3720bd39ce57, singleton1: da6cf60f-670a-4a86-8fd6-01b635f74225scope2: 29f127a7-baf5-4ab0-b264-fcced11d0729, transient2: 74c37151-6497-4223-b558-a4ffc1897d57, singleton2: da6cf60f-670a-4a86-8fd6-01b635f74225
We have noticed that we have got four Transient instances, two Scope instances, and one Singleton instance. What is the purpose? If you have used InstancePerRequest of Autofac in Mvc, you will know that some objects span multiple actions, services, and Repository in one request, for example, the most common DBContext can be an instance. This reduces the consumption of instance initialization and implements cross-Service transactions. (Note: all services that use EF in ASP. NET Core need to be registered as Scoped.) to implement this function, a Scope is shared within the lifecycle of the reqeust request. Iii. DI in ASP. application 3.1 in NET Core initializes ASP.. NET Core can be set in Startup. configure DI in ConfigureService of cs. You will be familiar with the IServiceCollection parameter.
public void ConfigureServices(IServiceCollection services){    services.AddTransient<ILoginService<ApplicationUser>,       EFLoginService>();    services.AddMvc();)

Some components of ASP. NET Core have provided some instance bindings. For example, AddMvc is the extension method added by Mvc Middleware on IServiceCollection.

public static IMvcBuilder AddMvc(this IServiceCollection services){    if (services == null)    {        throw new ArgumentNullException(nameof(services));    }    var builder = services.AddMvcCore();    builder.AddApiExplorer();    builder.AddAuthorization();    AddDefaultFrameworkParts(builder.PartManager);    ...}
3.2 In Controller, constructor or attribute can be used for injection. However, it is recommended to use constructor. This is also called explicit dependency.
 private ILoginService<ApplicationUser> _loginService;public AccountController(  ILoginService<ApplicationUser> loginService){  _loginService = loginService;}

We only need to write this parameter in the Controller's constructor, and ServiceProvider will inject it into us. This step is completed when the controller is initialized by Mvc. We will introduce it in detail later.

3.3 Use @ inject in View to declare it again and create an alias.
@using MilkStone.Services;@model MilkStone.Models.AccountViewModel.LoginViewModel@inject ILoginService<ApplicationUser>  loginService<!DOCTYPE html>3.4 using HttpContext to obtain an instance HttpContext, A RequestedService can also be used to obtain instance objects. However, this method is generally not recommended. At the same time, note that GetService <> is a normal method. By default, if you do not add the using of Microsoft. Extension. DependencyInjection, you do not need to call this method.
HttpContext.RequestServices.GetService<ILoginService<ApplicationUser>>();
4. How to replace the Autofac of other Ioc containers is also a good choice, but we must first figure out why we should replace the default DI container ?, What is the impact after replacement ?. The default Implementation of NET Core is sufficient for some small projects, and can be used even for large projects, but it may be troublesome, the reason is that only the most basic AddXXXX method is provided to bind the instance relationship. You need to add them one by one. If the project may need to add several hundred rows of such methods. If you are familiar with Autofac, the following code may have an image.
builder.RegisterGeneric(typeof(LoggingBehavior<,>)).As(typeof(IPipelineBehavior<,>)); builder.RegisterGeneric(typeof(ValidatorBehavior<,>)).As(typeof(IPipelineBehavior<,>));

This will bring convenience to our initialization. Let's take a look at how to replace Autofac with ASP. NET Core. We only need to change the returned value of ConfigureService in the Startup class from void to IServiceProvider. The returned result is an AutoServiceProvider.

public IServiceProvider ConfigureServices(  IServiceCollection services){    services.AddMvc();    // Add other framework services    // Add Autofac    var containerBuilder = new ContainerBuilder();    containerBuilder.RegisterModule<DefaultModule>();    containerBuilder.Populate(services);    var container = containerBuilder.Build();    return new AutofacServiceProvider(container);}
4.1. A major change is that the original life cycle InstancePerRequest of Autofac is no longer valid. As we have mentioned earlier, the entire request lifecycle is managed by ASP. NET Core, so Autofac is no longer valid. We can use instanceperlifetimesloud, which is also useful and corresponds to Scoped in ASP. NET Core DI. Reprinted: http://www.jessetalk.cn/2017/11/06/di-in-aspnetcore/

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.