In the previous chapters (middleware chapters), we mentioned dependency injection (Dependency injection), asp.net 5 formally implemented a fully functional dependency injection so that developers can develop more flexible component programs, MVC6 also uses the function of dependency injection to redesign the service injection function of controller and view; future dependency injection capabilities may also provide more APIs, and all of them need to be learned if they are not yet in contact with dependency injection.
In previous versions of dependency injection, Dependency injection was in MVC and in the IControllerFactory
Web API, and IHttpControllerActivator
in the new Asp.net5, dependency injection became the lowest base support, MVC, Routing, SIGNALR, Entity Framrwork is dependent on the injected IServiceProvider
interface, Microsoft gives the default implementation ServiceProvider
, and 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 the third party container.
For the common dependency type resolution and creation, Microsoft has defined 4 categories of lifecycle by default, respectively, as follows:
type |
Description |
Instance |
Only specific instance objects can be used at any time, and developers need to be responsible for initialization of the object. |
Transient |
Recreate an instance each time. |
Singleton |
Creates a single instance that returns the single Instance object each time it is called. |
Scoped |
Within the current scope, no matter how many times a call is made, an instance is created, and the instance is recreated again, similar to a single example within a specific action. |
Type registration and examples
The registration of a dependency injection type is typically in a program-initiated entry, such as in the 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 use interfaces and implementation classes as examples.
First declare an interface Itodorepository and implementation 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 declaration cycle types, it is recommended to implement several classes, such as TodoRepository2, TodoRepository3, TODOREPOSITORY4, etc. for demonstration purposes.
The
Then registers the interface itodorepository types and corresponding implementation classes within the Configureservices method, in this case registering different implementation classes based on different lifecycles, as shown in the following example:
//registration singleton mode, the example of the Itodorepository interface throughout the application lifecycle is a single instance of the 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 lifecycle is a single instance of the fixed initialization TodoRepository2 services.
Addinstance<itodorepository> (New TodoRepository2 ()); Services. Addinstance (typeof (Itodorepository), New TodoRepository2 ()); Equivalence form//register scope type, example of itodorepository within a specific scope is TodoRepository3 services.
Addscoped<itodorepository, todorepository3> (); Services. Addscoped (typeof (Itodorepository), typeof (TodoRepository3));//equivalent form//Get the Itodorepository instance, TodoRepository4 class services are instantiated once each time.
Addtransient<itodorepository, todorepository4> (); Services. AddTransient (typeof (Itodorepository), typeof (Todorepository))//equivalent form//If the class to be injected has no interface, you can inject it directly into your own type, for example: services.
Addtransient<logginghelper> ();
There are currently three types of dependency injection in MVC, namely, controller constructors, attributes, and inject forms in view. Where the constructor injection is the same as in previous MVC, the sample code is as follows:
public class Todocontroller:controller
{
private readonly itodorepository _repository;
The dependency injection framework automatically finds an example of a Itodorepository implementation class that is assigned to the constructor public
Todocontroller (itodorepository repository)
{
_ repository = repository;
}
[HttpGet]
Public ienumerable<todoitem> GetAll ()
{return
_repository. AllItems; This object can be used here.
}
Property injection, you [FromServices]
can implement an automatic fetch instance by adding a property to the attribute.
The public class Todocontroller:controller
{
//Dependency Injection framework automatically finds an example of the Itodorepository implementation class assigned to the property
[Fromservices] Public
itodorepository Repository {get; set;}
[HttpGet]
Public ienumerable<todoitem> GetAll ()
{return
repository.allitems;
}
}
Note: This way, currently applies only to controller and subclasses, not to ordinary classes
Also: In this way, you can get more system instance objects, such as ,,,,, ActionContext
HttpContext
HttpRequest
HttpResponse
ViewDataDictionary
and ActionBindingContext
.
In the view, you can @inject
implement an instance extraction of the injection type by using the keyword, as shown 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 of it, which is IServiceProvider
currently available in the following ways (but with a different range):
var provider1 = this. Request.HttpContext.ApplicationServices; Current application registered service var
provider2 = context.requestservices//Controller, service var provider3 registered in the current request scope
= Resolver; In controller
The GetService and Getrequiredservice methods are then used to get an instance of the specified type, as shown in the following example:
var _repository1 = Provider1. GetService (typeof (Itodorepository));
var _repository2 = Provider1. Getservice<logginghelper> ()///equivalent form
///above 2 objects may be empty
var _repository3 = provider1. Getrequiredservice (typeof (Itodorepository));
var _repository4 = Provider1. Getrequiredservice<logginghelper> ()///equivalence form
//The above 2 objects are definitely not empty, because if it is empty, it will automatically throw out the exception
Dependency Injection of ordinary classes
In the new version of ASP.NET5, we support not only the above-mentioned interface-class dependency injection, but also ordinary types of dependency injection, such as our life is 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 normal class, the registration usage should be as follows:
Services. Configure<appsettings> (App =>
{
app). Sitetitle = "a";
};
When used, you need to get an instance of the IOptions<AppSettings>
type, and then its Options property is an instance of appsettings, the code reads as follows:
var appSettings = app. Applicationservices.getrequiredservice<ioptions<appsettings>> (). Options;
Of course, we can also use syntax in the view to @inject
get the instance, the sample code is as follows:
@inject ioptions<appsettings> AppSettings
<title> @AppSettings .options.sitetitle</title>
Dependency injection based on scope life cycle
The common scope Dependency Injection
An instance based on scope scope needs to create a scope and then get a specific instance within that scope, so let's 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 the scope:
var serviceprovider = Resolver;
var scopefactory = serviceprovider.getservice<iservicescopefactory> (); Get the scope factory class
using (var scope = Scopefactory.createscope ())//Create a scope scope
{
var containerscopedservice = Serviceprovider.getservice<itodorepository> (); Gets the normal instance
var ScopedService1 = scope. Serviceprovider.getservice<itodorepository> (); Gets an instance of the current scope
thread.sleep (m);
var scopedService2 = scope. Serviceprovider.getservice<itodorepository> (); Gets the instance of the current scope
Console.WriteLine (Containerscopedservice = = ScopedService1);//output: False
Console.WriteLine ( ScopedService1 = = ScopedService2); Output: True
}
In addition, scope can be nested, and the instances of nested internal and external scopes are also different, with the instance code as follows:
var serviceprovider = Resolver;
var outerscopefactory = serviceprovider.getservice<iservicescopefactory> ();
using (var outerscope = Outerscopefactory.createscope ())//external scope scope
{
var innerscopefactory = Outerscope.serviceprovider.getservice<iservicescopefactory> ();
using (var innerscope = Innerscopefactory.createscope ())//Internal scope scope
{
var outerscopedservice = Outerscope.serviceprovider.getservice<itodorepository> ();
var innerscopedservice = innerscope.serviceprovider.getservice<itodorepository> ();
Console.WriteLine (Outerscopedservice = = Innerscopedservice); Output: False
}
}
Scope Dependency injection based on HTTP request
In many previous popular di containers, it is very popular to keep a single instance object within the request scope for each request, which means that an object instance of one type is created once per request, which can greatly improve performance.
In Asp.net5, a scope Dependency injection based on HTTP requests is implemented through one ContainerMiddleware
, and when called, a middleware di container is created that replaces the default Di container already in the current request. In the pipeline, all subsequent middleware will use this new Di container, which ends when the request is finished, and the scope ContainerMiddleware
is destroyed, and the instance objects created within that scope are destroyed and released.
ContainerMiddleware
The timeline diagram looks like this:
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 common classes
Current generic class Dependency injection, only supports constructors, such as we are 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);
}
You use this instance by passing in the parameters of the class in the constructor ITodoRepository
, and you need to register the class in the Di container first, as follows:
Services. Addscoped<itodorepository, todorepository> ();
Services. Addsingleton<testservice> ();
You can then invoke the following statement to use:
var service = serviceprovider.getrequiredservice<testservice> ();
Also, note that in the current situation, you cannot use the [FromServices]
Dependency injection feature, for example, if the following code gets TestService2
an error while getting an instance:
public class TestService2
{
[fromservices] public
itodorepository Repository {get; set;}
public void Show ()
{
Console.WriteLine (repository.allitems);
}
}
Get HttpContext instance in common class
In the MVC6, We have no way to get the context object by httpcontent.current, so it is problematic to use it in the ordinary class, and in order to use the context object in the ordinary class, we need to get the HttpContext instance through dependency injection, which is provided by Microsoft in Asp.net5 IHttpContextAccessor
interface is used to get the context object. That is, we can put arguments of that type in the constructor to get the context instance, as follows:
public class TestService3
{
private ihttpcontextaccessor _httpcontextaccessor;
Public TestService3 (Ihttpcontextaccessor httpcontextaccessor)
{
_httpcontextaccessor = Httpcontextaccessor ;
}
public void Show ()
{
var HttpContext = _httpcontextaccessor.httpcontext;//Gets the context object instance
Console.WriteLine (HttpContext.Request.Host.Value);
}
and use, 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 data that is supported by more than one Di container as a parameter.
Using a third party di container
At present,. Netcore is not supported and can only be used on the fully functional version of the. NET framework, so you need to be aware of it when you use it. The replacement of the third party di container is usually done in the Startup.cs configure method, and replaced at the beginning of the method so that subsequent middleware will use the associated dependency injection function.
First you 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 to register here
var builder = new Containerbuilder ();//construct Container build class
builder. Populate (services)//routes existing services to the AUTOFAC Management collection
IContainer container = Builder. Build ();
Return container. Resolve<iserviceprovider> ()//return AUTOFAC implemented 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 and wait for Microsoft to resolve it.
In addition, there is a way, Microsoft's current instance project has not been disclosed, by analyzing some code, we can find that in the program in charge of the execution of the Microsoft.AspNet.Hosting
StartupLoader.cs
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 source analysis it is found that when the method is parsed according to the contract, ConfigureServices
it first determines if there is a return type IServiceProvider
, and if so, executes the method, using the new instance returned with the return IServiceProvider
; , and then continue to find void
a method of the type ConfigureServices
. So, in this way, we can replace the Di container of a third party with the following instance code:
Configureservices method Public
IServiceProvider configureservices (iservicecollection services) with the void type needed to be removed first
{
var builder = new Containerbuilder ();//construct Container build class
builder. Populate (services); Routes existing services to the AUTOFAC Management collection
IContainer container = Builder. Build ();
Return container. Resolve<iserviceprovider> (); Returns the IServiceProvider of the AUTOFAC implementation
}
In this way, you can use the AUTOFAC approach to manage dependent 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 of 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.
Before doing anything, be sure to register all dependent types in advance at the entry point of the program. Instead of using the IServiceProvider interface directly, you can explicitly add the types you need to rely on in the constructor to allow the dependency injection engine to parse the instance itself, and use the abstract factory once reliance is difficult to manage. Programming based on interfaces, rather than on 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