Interpretation of ASP. NET 5 & MVC6 series (7): dependency Injection

Source: Internet
Author: User

Interpretation of ASP. NET 5 & MVC6 series (7): dependency Injection

In the previous section (Middleware), we mentioned the Dependency Injection function, ASP. NET 5 officially implements full-featured dependency injection so that developers can develop more flexible component programs, MVC6 also uses the dependency injection function to re-design the service injection functions of Controller and View. In the future, the dependency injection function may provide more APIs, if you haven't started to get started with dependency injection, you have to learn about it.

In previous versions of the dependency injection function, the dependency injection entries include IControllerFactory in MVC and IHttpControllerActivator in the Web API. In the new version of ASP. in NET5, dependency injection becomes the underlying basic support. MVC, Routing, SignalR, Entity Framrwork, and so on all depend on the injected IServiceProvider interface, for this interface, Microsoft provides the default implementation ServiceProvider, as well as Ninject and AutoFac packages. Of course, you can also use other third-party dependency injection containers, such as Castle Windsor; once a third-party container is applied, all dependency parsing will be routed to the third-party container.

For the parsing and creation of common dependency types, Microsoft defines four types of lifecycles by default, respectively:

 

Type registration and Examples

The registration of the dependency injection type is generally in the program Startup entry, such as ConfigureServices in Startup. cs. The main purpose of this class is to register the dependency injection type. Because dependency injection is mainly embodied in interface programming, in this example, I use interfaces and implementation classes as an example.

First, declare an interface ITodoRepository and the implementation class TodoRepository1. The Code is as follows:

public interface ITodoRepository{    IEnumerable
 
   AllItems { get; }     void Add(TodoItem item);     TodoItem GetById(int id);     bool TryDelete(int id); }  public class TodoItem {     public int Id { get; set; }     public string Name { get; set; } }  public class TodoRepository : ITodoRepository {     readonly List
  
    _items = new List
   
    ();      public IEnumerable
    
      AllItems     {         get { return _items; }     }      public TodoItem GetById(int id)     {         return _items.FirstOrDefault(x => x.Id == id);     }      public void Add(TodoItem item)     {         item.Id = 1 + _items.Max(x => (int?)x.Id) ?? 0;         _items.Add(item);     }      public bool TryDelete(int id)     {         var item = GetById(id);          if (item == null) { return false; }          _items.Remove(item);          return true;     } }
    
   
  
 

To demonstrate different types of declaration cycles, we recommend that you implement several classes, such as TodoRepository2, TodoRepository3, and TodoRepository4. Then, register the interface ITodoRepository type and corresponding implementation class in the ConfigureServices method. In this example, different implementation classes are registered according to different lifecycles. The specific example is as follows:

 
 
 
  1. // Register the singleton mode. The example of the ITodoRepository interface throughout the application cycle is a singleton instance of TodoRepository1.
  2. Services. AddSingleton

Currently, dependency injection is used in MVC in three ways: Controller constructor, attribute, and Inject in View. The constructor injection is the same as that in MVC. The sample code is as follows:

 
 
 
  1. Public class TodoController: Controller
  2. {
  3. Private readonly ITodoRepository _ repository;
  4.  
  5. /// The dependency injection framework automatically finds the example of the ITodoRepository implementation class and assigns it to the constructor.
  6. Public TodoController (ITodoRepository repository)
  7. {
  8. _ Repository = repository;
  9. }
  10.  
  11. [HttpGet]
  12. Public IEnumerable GetAll () {return _ repository. AllItems; // you can use this object here }}

Property injection is performed by adding an [Activate] attribute to the attribute to automatically obtain the instance.

 
 
  1. Public class TodoController: Controller
  2. {
  3. // The dependency injection framework automatically finds the example of the ITodoRepository implementation class and assigns it to this attribute.
  4. [Activate]
  5. Public ITodoRepository Repository {get; set ;}
  6.  
  7. [HttpGet]
  8. Public IEnumerable GetAll () {return Repository. AllItems ;}}

Note: This method is applicable only to Controller and subclass, and not to common classes.

In addition, you can obtain more system instance objects, such as ActionContext, HttpContext, HttpRequest, HttpResponse, ViewDataDictionary, and ActionBindingContext. In the view, the @ inject keyword can be used to extract injection-type instances, for example:

 
 
  1. @using WebApplication1 
  2. @inject ITodoRepository repository 
  3. @repository.AllItems.Count() 

The most common way to use IServiceProvider is to obtain the IServiceProvider instance. The methods to obtain this IServiceProvider instance are as follows (but the range is different ):

 
 
  1. Var provider1 = this. Request. HttpContext. ApplicationServices; the Service registered in the current application
  2. Var provider2 = Context. RequestServices; // The Service registered in the current request scope in the Controller
  3. Var provider3 = Resolver; // Controller

The GetService and GetRequiredService methods are used to obtain instances of the specified type, for example:

Var _ repository1 = provider1.GetService (typeof (ITodoRepository); var _ repository2 = provider1.GetService
 
  
(); // Equivalent form // the above two objects may be empty var _ repository3 = provider1.GetRequiredService (typeof (ITodoRepository); var _ repository4 = provider1.GetRequiredService
  
   
(); // Equivalent form // the above two objects are certainly not empty, because if they are empty, an exception is automatically thrown.
  
 

Dependency injection for common classes

In the new version of ASP. NET5, it not only supports the interface class dependency injection we mentioned above, but also supports the normal type of dependency injection. For example, we have a common class, for example:

 
 
  1. public class AppSettings 
  2.     public string SiteTitle { get; set; } 

To ensure that there is no parameter constructor in the above common classes, the registration usage should be as follows:

 
 
  1. services.Configure(app => {     app.SiteTitle = "111"; }); 

When using the API, You need to obtain an IOptions instance, and its Options attribute is the instance of the deleettings. The Code is as follows:

 
 
  1. var appSettings = app.ApplicationServices.GetRequiredService 

Of course, we can also use the @ inject syntax in the view to obtain the instance. The sample code is as follows:

 
 
  1. @inject IOptions<AppSettings> AppSettings 
  2. <title>@AppSettings.Options.SiteTitle</title> 

Dependency Injection Based on Scope Lifecycle

Normal Scope dependency Injection

When creating a Scope-based instance, you must first create a Scope and then obtain a specific instance in the Scope. Let's look at an example and verify it. First, register the dependency injection type. The Code is as follows:

services.AddScoped

Create a scope and obtain the instance in the scope:

Var serviceProvider = Resolver; var scopeFactory = serviceProvider. GetService
 
  
(); // Obtain the Scope factory class using (var scope = scopeFactory. createlist () // create a Scope {var containerScopedService = serviceProvider. GetService
  
   
(); // Obtain the common instance var scopedService1 = scope. ServiceProvider. GetService
   
    
(); // Obtain the current Scope instance Thread. Sleep (200); var scopedService2 = scope. ServiceProvider. GetService
    
     
(); // Obtain the instance Console of the current Scope. writeLine (containerScopedService = scopedService1); // output: False Console. writeLine (scopedService1 = scopedService2); // output: True}
    
   
  
 

In addition, Scope can also be nested, And the instances obtained by nested internal and external scopes are also different. The instance code is as follows:

Var serviceProvider = Resolver; var outerScopeFactory = serviceProvider. GetService
 
  
(); Using (var outerScope = outerScopeFactory. createlist () // external Scope {var innerScopeFactory = outerScope. ServiceProvider. GetService
  
   
(); Using (var innerScope = innerScopeFactory. createlist () // internal Scope {var outerScopedService = outerScope. ServiceProvider. GetService
   
    
(); Var innerScopedService = innerScope. ServiceProvider. GetService
    
     
(); Console. WriteLine (outerScopedService = innerScopedService); // output: False }}
    
   
  
 

Scope dependency Injection Based on HTTP requests

In many popular DI containers, it is very popular to retain a single instance object in the request scope for each request, that is, during each request, a type of object instance is created only once, which can greatly improve the performance.

In ASP. in NET5, Scope dependency Injection Based on HTTP requests is implemented through a ContainerMiddleware. When the Middleware is called, a DI container with a limited Scope is created, replaces the existing default DI container in the current request. In this Pipeline, all subsequent Middleware will use this new DI container. After the request completes the entire Pipeline, the ContainerMiddleware function ends, and the scope will be destroyed, in addition, all instance objects created in this scope will be destroyed and released.

The time sequence diagram of ContainerMiddleware is as follows:

The usage is as follows:

 
 
  1. app.Use(new Func 

Dependency injection for common classes

Currently, only constructor is supported for dependency injection of common classes. For example, we define a TestService class. The Code is as follows:

 
 
  1. public class TestService 
  2.     private ITodoRepository _repository; 
  3.     public TestService(ITodoRepository r) 
  4.     { 
  5.         _repository = r; 
  6.     } 
  7.  
  8.     public void Show() 
  9.     { 
  10.         Console.WriteLine(_repository.AllItems); 
  11.     } 

You can use this instance by passing in the ITodoRepository class parameter in the constructor. When using this class, you must first register it in the DI container. The Code is as follows:

 
 
  1. services.AddScoped 

Then, you can use the following statement:

 
 
  1. var service = serviceProvider.GetRequiredService(); 

In addition, you must note that [Activate] cannot be used to use the dependency injection function. For example, the following code may cause an error when obtaining the TestService2 instance:

 
 
  1. public class TestService2 
  2.     [Activate] 
  3.     public ITodoRepository Repository { get; set; } 
  4.     public void Show() 
  5.     { 
  6.         Console.WriteLine(Repository.AllItems); 
  7.     } 

Get HttpContext instance in common class

In MVC6, we cannot use HttpContent. current to get the context object, so when using it in a common class, there will be a problem. To use this context object in a common class, you need to get the HttpContext instance through dependency injection, microsoft in ASP. in NET5, The IHttpContextAccessor interface is provided to obtain the context object. That is to say, we can put this type of parameter in the constructor to obtain the context instance. The Code is as follows:

 
 
  1. Public class TestService3
  2. {
  3. Private IHttpContextAccessor _ httpContextAccessor;
  4. Public TestService3 (IHttpContextAccessor httpContextAccessor)
  5. {
  6. _ HttpContextAccessor = httpContextAccessor;
  7. }
  8.  
  9. Public void Show ()
  10. {
  11. Var httpContext = _ httpContextAccessor. HttpContext; // gets the context object instance
  12. Console. WriteLine (httpContext. Request. Host. Value );
  13. }
  14. }

In use, you can use the following statement directly. The Code is as follows:

 
 
  1. var service = serviceProvider.GetRequiredService(); service.Show(); 

Tip: In the constructor of a common class, data supported by multiple DI containers can be imported as a parameter.

Use a third-party DI container

Currently,. NETCore is not supported and can only be used on the. NET framework of the Full-function version. Therefore, pay attention to it when using it. Third-party DI container replacement is usually carried out in the Configure method of Startup. cs, and is replaced at the beginning of the method, so that later Middleware will use the relevant dependency injection function.

First, introduce a third-party container. Take Autofac as an example, introduce Microsoft. Framework. DependencyInjection. Autofac, and then add the replacement code in the following example:

 
 
  1. App. UseServices (services =>
  2. {
  3. Services. AddMvc (); // AddMvc must be registered here
  4. Var builder = new ContainerBuilder (); // construct a container build class
  5. Builder. Populate (services); // route existing Services to the management set of Autofac
  6. IContainer container = builder. Build ();
  7. Return container. Resolve (); // return IServiceProvider implemented by AutoFac });

Note: when using the above method, the Mvc registration code services. AddMvc () must be moved from ConfigureServices to this expression; otherwise, an exception will be reported, waiting for Microsoft to solve the problem. In addition, there is another way that Microsoft's current instance project has not yet been made public. By analyzing some code, we can find that in Microsoft. aspNet. startupLoader in the Hosting program. cs is responsible for executing the program entry point. In this file, we know that the first step is to call Startup. the ConfigureServices method in cs, and then call the Configure method. We can see that the returned value of ConfigureServices In the example is of the void type, but the source code analysis shows that when the ConfigureServices method is parsed according to the Conventions, first, determine whether the returned type is IServiceProvider. If yes, execute this method and use the new IServiceProvider instance returned in the return; if no, continue to find the ConfigureServices method of void type. Therefore, we can replace the third-party DI container in this way. The instance code is as follows:

// Delete the void ConfigureServices method public IServiceProvider ConfigureServices (IServiceCollection services) {var builder = new ContainerBuilder (); // construct the container build class builder. populate (services); // routes existing Services to the IContainer container = builder in the Autofac management set. build (); return container. resolve
 
  
(); // Returns the IServiceProvider implemented by AutoFac}
 

In this way, you can use the Autofac method to manage dependency types, as in the past. The example is as follows:

public class AutofacModule : Module{    protected override void Load(ContainerBuilder builder)    {        builder.Register(c => new Logger())            .As
 
  ()             .InstancePerLifetimeScope();          builder.Register(c => new ValuesService(c.Resolve
  
   ()))             .As
   
    ()             .InstancePerLifetimeScope();     } }
   
  
 

Address: https://github.com/aspnet/Hosting/blob/dev/src/Microsoft.AspNet.Hosting/Startup/StartupLoader.cs

Another case about Autofac integration: http://alexmg.com/autofac-4-0-alpha-1-for-asp-net-5-0-beta-3/

Best practices

When using dependency injection, we should follow the following best practices.

Before doing anything, register all the dependency types in advance at the program entry point.

Avoid directly using the IServiceProvider interface. On the contrary, explicitly add the type of dependency to the constructor so that the dependency injection engine can parse the instance by itself. Once dependency is difficult to manage, the abstract factory is used.

Programming Based on interfaces rather than implementation.

Reference 1: http://social.technet.microsoft.com/wiki/contents/articles/28875.dependency-injection-in-asp-net-vnext.aspx

Reference 2: http://blogs.msdn.com/ B /webdev/archive/2014/06/17/dependency-injection-in-asp-net-vnext.aspx

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.