依賴注入一直是asp.net web框架(Web API,SignalR and MVC)中不可或缺的一部分,可是在之前,這個框架都是各自升級,都有各自的依賴注入實現方式,即便
Katana項目想經過
Owin將這些項目鏈接起來,你仍是要作統一的容器去支持它們,如今事情有所變化了。
抽象
asp.net團隊決定提供依賴注入功能經過提煉最流行的IOC容器中最核心的功能,而後讓不一樣的組件實現這些接口來實現依賴注入。
IServiceProvider
這個最主要的接口,開發均可以經過這個接口去檢索Ioc容器中已經註冊過的服務,這個接口只有一個方法而已:
GetService(Type), 相似於Aotufact裏的
Container.Resolve<service>,或者Ninject裏的
Kernel.Get<service>.
全部的MiddleWare有兩種方式能夠得到所要的
IserviceProvider.
- 應用程序級別:經過HttpContext.ApplicationServices 屬性提供給 MiddleWare
- 請求級別:經過HttpContext.RequestServices屬性提供給MiddleWare,這個範圍的ServiceProvider經過一個隱式的MIddleWare在最開始的請求管理道中被建立,在請求最後返回響應前被釋放。
所有的MiddleWare可經過這些屬性去解決他們的服務,例如ASP.NET MVC MIddleWare會經過
RequestServices去建立Controllers和他們的依賴
IServiceScope
這個接口是容器的一個包裝,主要做用是在請求結束後去釋放容器
- IServiceProvider
- Dispose()
IServiceScopeFactory
很簡單的一個接口,只有一個返回
IServiceScope的方法
CreateServiceScope()
因此若是你須要實現一個IOC 容器,就要本身實現以上的接口。
ServiceLifetime
- Singleton 單個實例在整個應用程序中
- Scoped 單個實例在範圍窗口內
ServiceDescriptor
註冊服務的描述信息,
- ServiceType 用來替換具體實現類的接口,Type類型
- ImplementationType 上面這個接口的具體實現類型,Type類型
- Lifetime 服務的生命週期,Singleon,Scoped 或者 Transient
- ImplementaionFactory 類型Func<IServiceProvider,Object>, 在一些場景中,開發人員但願提供一個工廠方法去建立具體實現類。他們之間是互相獨立的,若是你提供了ImplementationType,那不能再提供ImpletentaionFactory.
- ImplementationInstance 具體事例
Registering Services
如今註冊您的服務,ASPNET5但願您 的
Startup類中有個叫
ConfigureServices的方法名,帶有一系列
ServiceDescriptors包裹在
IServiceCollection,並什麼都不返回。你所有隻要作的只是建立一系列
ServiceDescriptor,並添加他們到集合,web應用程序會在稍後加載他們並註冊到容器中
publicvoidConfigureServices(IServiceCollection services)
{
var serviceDescriptor = newServiceDescriptor(typeof(IBankManager), typeof(BankManager), ServiceLifetime.Transient);
services.Add(serviceDescriptor);
// Add MVC services to the services container.
services.AddMvc();
}
備註
:
建立serviceDescriptor有一些繁瑣, 這就是你看到一些中間件用擴展方法去建立ServiceDescriptors, 像「service.AddMvc()」
下面的僞陳述說明服務啓動和ServiceProvider是怎樣建立的,相應的代碼能夠在
HostingEngine.Start中找到
應用程序是如何啓動的。
- Hosting engine 會建立一個IServiceCollection,是ServiceDescriptor的集合對象
- Hosting engine 會添加所有它所須要的服務
- Hosting engine 會確認在程序集中有個Startup類,並有個ConfigureServices方法
- Hosting engine 會去加載這個方法,並傳遞IServiceCollection
- Startup類中的ConfigureSerivces會添加應用程序須要的服務至集合中
- Hosting engine 接着會建立 DefaultServiceProvider(ICO容器),並註冊服務集合中IServiceCollection中的服務
- Hosting engine 會建立一個應用程序構建器(IApplicationBuilder)並給IApplicationBuilder.ApplicationServices分配一,個新的Service Provider,並進一步的使用它
- Hosting engin 在Startup.Configuer執行前會建立一個RequestServicesContainerMiddleware中間件,我會在稍後介紹它
- Hosting engine 會執行Startup類中 Configure方法,並傳遞Application Builder去建立中間件。若是須要Service Provider可經過ApplicationServices屬性去建立中間件。
運行請求
當第一個請求過來時,Httpcontext會被建立並傳給第一個Middleware中的Invokde方法中,並會一直傳到整個Middleware.可是在處理第一個中間件以前,Application Builder 's Service Provider 會被分配到HttpContext.ApplicationServices,確保整個中間件均可以經過這個屬性得到須要的服務,不過,你應該記住這是一個應用程序級別的服務提供者,而且依賴於你選擇的Ioc容器,若是你使用了它,你的對象可能會一直駐留在應用程序整個生活週期中.git
Note:
in theory, 理論上,做爲一個應用開發人員,你不該該直接去使用這個服務提供者,若是要用建議用Service Locator 模式
好吧,這是一個應用程序級別的服務提供者,那有沒有每次請求範圍裏的的服務提供者?github
在上面列出來的第8步中,咱們提到了Hosting engine 會在管道開始以前會建立一個
RequestServicesContainerMiddleware中間件,並會第一時間去執行它
public async Task Invoke(HttpContext httpContext)
{
using(varcontainer = RequestServicesContainer.EnsureRequestServices(httpContext, _services))
{
await_next.Invoke(httpContext);
}
}
回到上面的請求執行中,服務會建立Httpcontext,並分派給 Application-level Service Provider給HttpContext.ApplicationServices,接着會調用第一個中間件,就是上面的RequestServicesContainerMiddleware,它所作的事情就是建立一個範圍內的服務提供者並在請求結束時銷燬它
它的僞實現是:
- 請求正在被RequestServiceContainerMiddleware處理
- Invoke方法會經過application-level Service Provider建立一個IServiceScopeFactory.
- IServiceScopeFactory會建立一個範圍容器(scoped container)
- scoped container會被指派給屬性HttpContext.RequestServices
- Invoke方法調用接下來的Middleware
- 當所有的Middleware都被執行完後,並返回到了RequestServiceContainerMiddleware,Scoped Container會被銷燬經過"Using"
private async Task InvokeActionAsync(RouteContext context, ActionDescriptor actionDescriptor)
{
var services = context.HttpContext.RequestServices;
Debug.Assert(services != null);
var actionContext = new ActionContext(context.HttpContext, context.RouteData, actionDescriptor);
var optionsAccessor = services.GetRequiredService<IOptions<MvcOptions>>();
actionContext.ModelState.MaxAllowedErrors = optionsAccessor.Options.MaxModelValidationErrors;
var contextAccessor = services.GetRequiredService<IScopedInstance<ActionContext>>();
contextAccessor.Value = actionContext;
var invokerFactory = services.GetRequiredService<IActionInvokerFactory>();
var invoker = invokerFactory.CreateInvoker(actionContext);
if (invoker == null)
{
LogActionSelection(actionSelected: true, actionInvoked: false, handled: context.IsHandled);
throw new InvalidOperationException(
Resources.FormatActionInvokerFactory_CouldNotCreateInvoker(
actionDescriptor.DisplayName));
}
await invoker.InvokeAsync();
}