Interpreting ASP 5 & MVC6 Series (7): Dependency Injection

Source: Internet
Author: User
Tags hosting

Original: Interpretation of ASP. 5 & MVC6 Series (7): Dependency Injection

In the previous chapters (middleware), we mentioned the dependency injection function (Dependency injection), and ASP. NET 5 formally implemented a fully functional implementation of dependency injection so that developers could develop more resilient component programs MVC6 also re-engineered the service injection capabilities of controller and view using the functionality of dependency injection, and the future Dependency injection feature may also provide more APIs, all of which have to be learned if you haven't started contacting dependency injection.

In previous versions of dependency injection, the ingress of dependency injection was in MVC and in the IControllerFactory Web API, and IHttpControllerActivator in the new Asp.net5, dependency injection became the underlying foundation support, MVC, Routing, SignalR, Entity Framrwork depends on the interface of the dependency injection IServiceProvider , Microsoft gives the default implementation of the interface, ServiceProvider as well as the Ninject and AUTOFAC version of the packaging, of course, you can also use other third-party dependency injection containers, such as Castle Windsor, etc. ; Once a third-party container is applied, all dependency resolution is routed to that third-party container.

For the resolution and creation of common dependency types, Microsoft has defined 4 categories of lifecycles by default, as follows:

type Description
Instance Only specific instance objects can be used at any time, and developers need to be responsible for the initialization of the object.
Transient Recreate one instance at a time.
Singleton Create a singleton that returns the singleton every time you call.
Scoped Within the current scope, no matter how many times it is called, it is an instance, and the scope is created again, similar to a singleton within a particular action.
Type registration with example

Registrations that rely on injection types are typically in a program-initiated portal, such as Configureservices in Startup.cs, where the primary purpose of the class is to register the type of dependency injection. Because the main embodiment of dependency injection is interface programming, in this case, I have an example of an interface and an implementation class.

First declare an interface Itodorepository and implement class TodoRepository1, the code is as follows:

  public interface itodorepository{ienumerable<todoitem> 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<todoitem> _items = new list<todoitem> ();    Public ienumerable<todoitem> 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 claim periods, it is recommended to implement several classes, such as TodoRepository2, TodoRepository3, TodoRepository4, etc., for demonstration purposes.

Then register the interface itodorepository type and the corresponding implementation class within the Configureservices method, in this case the different implementation classes are registered according to different lifecycles, as shown in the following example:

 //register singleton mode, the example of Itodorepository interface throughout the application cycle is a singleton instance of TodoRepository1 Services.addsingleton <itodorepository, todorepository1> (); services.  Addsingleton (typeof (Itodorepository), typeof (TodoRepository1)); Equivalent form//register a specific instance model, the example of the Itodorepository interface throughout the application cycle is a single instance of fixed initialization good todorepository2services.addinstance< Itodorepository> (New TodoRepository2 ()); services.  Addinstance (typeof (Itodorepository), New TodoRepository2 ()); Equivalent//Register the type of the scope type, within a particular scope Itodorepository example is Todorepository3services.addscoped<itodorepository, Todorepository3> (); services. Addscoped (typeof (Itodorepository), typeof (TodoRepository3));//equivalent form//Get the Itodorepository instance, Each time you instantiate the TodoRepository4 class Services.addtransient<itodorepository, todorepository4> (); services. AddTransient (typeof (Itodorepository), typeof (Todorepository));//equivalent form//If the class to be injected does not have an interface, you can inject it directly into its own type, for example: services. Addtransient<logginghelper> ();  

There are currently three ways to use dependency injection in MVC, namely the controller's constructor, attributes, and inject form in view. Where the constructor injection is the same as in the previous MVC, the sample code is as follows:

public class TodoController : Controller{    private readonly ITodoRepository _repository;    /// 依赖注入框架会自动找到ITodoRepository实现类的示例,赋值给该构造函数    public TodoController(ITodoRepository repository)    {        _repository = repository;    }    [HttpGet]    public IEnumerable<TodoItem> GetAll()    {        return _repository.AllItems;  //这里就可以使用该对象了    }}

attribute injection, it is possible to get an instance automatically by adding an attribute to the attribute [Activate] .

public class TodoController : Controller{    // 依赖注入框架会自动找到ITodoRepository实现类的示例,赋值给该属性    [Activate]    public ITodoRepository Repository { get; set; }    [HttpGet]    public IEnumerable<TodoItem> GetAll()    {        return Repository.AllItems;    }}

Note: This approach, currently applicable only to controllers and subclasses, does not apply to normal class
At the same time: In this way, you can get more system instance objects, such as,,,,,, and so on ActionContext HttpContext HttpRequest HttpResponse ViewDataDictionary ActionBindingContext .

In the view, you can use @inject keywords to implement an injection type of instance extraction, as in the following example:

@using WebApplication1@inject ITodoRepository repository<div>    @repository.AllItems.Count()</div>

The most common way to use it is to get IServiceProvider an instance, and the way to get the IServiceProvider instance is as follows (but with a different range):

var provider1 = this.Request.HttpContext.ApplicationServices; 当前应用程序里注册的Servicevar provider2 = Context.RequestServices;  // Controller中,当前请求作用域内注册的Servicevar provider3 = Resolver; //Controller中

The GetService and Getrequiredservice methods are then used to obtain an instance of the specified type, as in the following example:

var _repository1 = provider1.GetService(typeof(ITodoRepository));var _repository2 = provider1.GetService<LoggingHelper>();//等价形式//上述2个对象可能为空var _repository3 = provider1.GetRequiredService(typeof(ITodoRepository));var _repository4 = provider1.GetRequiredService<LoggingHelper>();//等价形式//上述2个对象肯定不为空,因为如果为空的话,会自动抛异常出来
Dependency Injection of ordinary classes

In the new version of ASP.NET5, we support not only the dependency injection of the interface class mentioned above, but also the common type of dependency injection, such as our life, a common class, examples are as follows:

public class AppSettings{    public string SiteTitle { get; set; }}

To ensure that there is no parameter constructor for the above common class, the usage of the registration should be as follows:

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

When used, you need to obtain IOptions<AppSettings> an instance of the type, and then its Options property is an instance of appsettings, with the following code:

var appSettings = app.ApplicationServices.GetRequiredService<IOptions<AppSettings>>().Options;

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

@inject IOptions<AppSettings> AppSettings<title>@AppSettings.Options.SiteTitle</title>
Injection of general scope dependency injection based on dependency of scope life cycle

A scope-based instance creates a scope and then acquires a specific instance within that scope, and we take a look at an example and validate it. First, register the dependency injection type with the following code:

services.AddScoped<ITodoRepository, TodoRepository>();

Then create the scope and get the instance within that scope:

var serviceProvider = Resolver;var scopeFactory = serviceProvider.GetService<IServiceScopeFactory>(); //获取Scope工厂类using (var scope = scopeFactory.CreateScope())  // 创建一个Scope作用域{    var containerScopedService = serviceProvider.GetService<ITodoRepository>();  //获取普通的实例    var scopedService1 = scope.ServiceProvider.GetService<ITodoRepository>(); //获取当前Scope的实例    Thread.Sleep(200);    var scopedService2 = scope.ServiceProvider.GetService<ITodoRepository>(); //获取当前Scope的实例    Console.WriteLine(containerScopedService == scopedService1); // 输出:False    Console.WriteLine(scopedService1 == scopedService2); //输出:True}

In addition, scope can also be nested, nested internal and external scope of the obtained instance is not the same, the instance code is as follows:

var serviceProvider = Resolver;var outerScopeFactory = serviceProvider.GetService<IServiceScopeFactory>();using (var outerScope = outerScopeFactory.CreateScope()) //外部Scope作用域{    var innerScopeFactory = outerScope.ServiceProvider.GetService<IServiceScopeFactory>();    using (var innerScope = innerScopeFactory.CreateScope()) //内部Scope作用域    {        var outerScopedService = outerScope.ServiceProvider.GetService<ITodoRepository>();        var innerScopedService = innerScope.ServiceProvider.GetService<ITodoRepository>();        Console.WriteLine(outerScopedService == innerScopedService); // 输出:False    }}
Scope Dependency injection based on HTTP requests

In many of the previously popular Di containers, it was popular to keep a single-instance object within the request scope for each request, that is, one type of object instance is created only once per request, which can greatly improve performance.

In Asp.net5, a scope dependency injection based on an HTTP request is implemented by one ContainerMiddleware , and when called, a scoped di container is created to replace the default Di container already in the current request. In this pipeline, all subsequent middleware use the New Di container, and after the request has completed the entire pipeline pipeline, the ContainerMiddleware effect ends, and the scope is destroyed, and the instance objects created within that scope are destroyed and released.

ContainerMiddlewareThe timing diagram is as follows:

The use of the specific method is as follows:

app.Use(new Func<RequestDelegate, RequestDelegate>(nextApp => new ContainerMiddleware(nextApp, app.ApplicationServices).Invoke));
Dependency injection processing for ordinary classes

The current generic class of dependency injection, only support constructors, such as we set in a TestService class, the code is as follows:

public class TestService{    private ITodoRepository _repository;    public TestService(ITodoRepository r)    {        _repository = r;    }    public void Show()    {        Console.WriteLine(_repository.AllItems);    }}

This instance is used by passing in the parameters of the class in the constructor, which is required to register the class with the ITodoRepository di container first, and the code is as follows:

services.AddScoped<ITodoRepository, TodoRepository>();services.AddSingleton<TestService>();

Then call the following statement to use:

var service = serviceProvider.GetRequiredService<TestService>();

In addition, it is important to note that, in the present case, you cannot use the [Activate] dependency injection function, for example, the following code TestService2 will have an error in getting the instance:

public class TestService2{    [Activate]    public ITodoRepository Repository { get; set; }    public void Show()    {        Console.WriteLine(Repository.AllItems);    }}
Getting HttpContext instances in a common class

In the MVC6, We have no way to get the context object through httpcontent.current, so there is a problem when it is used in the ordinary class, to use the context object in the normal class, we need to get the HttpContext instance through dependency injection, and Microsoft in Asp.net5 provides IHttpContextAccessorthe interface is used to get the context object. That is, we can put the parameter of the type in the constructor to get the context instance, the code is as follows:

public class TestService3{    private IHttpContextAccessor _httpContextAccessor;    public TestService3(IHttpContextAccessor httpContextAccessor)    {        _httpContextAccessor = httpContextAccessor;    }    public void Show()    {        var httpContext = _httpContextAccessor.HttpContext;//获取上下文对象实例        Console.WriteLine(httpContext.Request.Host.Value);    }}

And the use of the time, then directly through the following statements can be, the code is as follows:

var service = serviceProvider.GetRequiredService<TestService3>();service.Show();

Tip: In a normal class constructor, you can pass in multiple DI containers that support data similar to the parameters.

Using a third-party di container

At present,. Netcore is not supported and can only be used on the full-featured version of the. NET framework, so you need to be careful when you use it. The replacement of a third-party di container is usually done in the Configure method of the Startup.cs, and is replaced at the beginning of the method so that subsequent middleware will use the associated dependency injection function.

The first step is to 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:

app.UseServices(services =>{    services.AddMvc();// AddMvc要在这里注册    var builder = new ContainerBuilder();// 构造容器构建类    builder.Populate(services);//将现有的Services路由到Autofac的管理集合中    IContainer container = builder.Build();    return container.Resolve<IServiceProvider>();//返回AutoFac实现的IServiceProvider});

Note that when using the above method, the registration code of MVC services.AddMvc(); must be moved from the ConfigureServices middle to the expression, otherwise it will report an exception, waiting for Microsoft to resolve.

In addition, there is a way, Microsoft's current instance of the project has not been disclosed, through the analysis of some code, we can find that in the Microsoft.AspNet.Hosting program is StartupLoader.cs responsible for the execution of the program entry point, in the file, we know that the first call Startup.cs in the ConfigureServices method, and then call the Configure method We can see that the return value in the example ConfigureServices is of type void, but in the source analysis it is found that when parsing a method according to ConfigureServices the contract, it first determines whether there is a return type IServiceProvider , and if so, executes the method, using the new instance returned in the return IServiceProvider ; , and then continue to find void methods for the type ConfigureServices . So, we can replace the third-party di container in this way, the instance code is as follows:

// 需要先删除void类型的ConfigureServices方法public IServiceProvider ConfigureServices(IServiceCollection services){    var builder = new ContainerBuilder();  // 构造容器构建类    builder.Populate(services);  //将现有的Services路由到Autofac的管理集合中    IContainer container = builder.Build();    return container.Resolve<IServiceProvider>(); //返回AutoFac实现的IServiceProvider}

In this way, you can use AUTOFAC to manage dependency types as usual, as shown in the following example:

public class AutofacModule : Module{    protected override void Load(ContainerBuilder builder)    {        builder.Register(c => new Logger())            .As<ILogger>()            .InstancePerLifetimeScope();        builder.Register(c => new ValuesService(c.Resolve<ILogger>()))            .As<IValuesService>()            .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 adhere to the following best practices.

    1. Before doing anything, be sure to register all dependency types in advance at the entry point of the program.
    2. Avoid using the IServiceProvider interface directly, instead, explicitly add types that need to be relied upon in the constructor, allowing the dependency injection engine to parse the instance itself, and use the abstract factory once the dependencies are difficult to manage.
    3. Programming based on an interface rather than an implementation-based program.

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

Synchronization and recommendations

This article has been synchronized to the Catalog index: Interpreting ASP. & MVC6 Series

Interpreting ASP 5 & MVC6 Series (7): Dependency Injection

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.