【轉載】ASP.NET Core 依賴註入
本文轉自:http://www.jessetalk.cn/2017/11/06/di-in-aspnetcore/
為什麽要寫這個博客
DI在.NET Core裏面被提到了一個非常重要的位置, 這篇文章主要再給大家普及一下關於依賴註入的概念,身邊有工作六七年的同事還個東西搞不清楚。另外再介紹一下.NET Core的DI實現以及對實例生命周期的管理(這個是經常面試會問到的問題)。最後再給大家簡單介紹一下在控制臺以及Mvc下如何使用DI,以及如何把默認的Service Container 替換成Autofac。
1.1依賴
1.2 什麽是註入
private ILoginService<ApplicationUser> _loginService; public AccountController() { _loginService = new EFLoginService() }
private ILoginService<ApplicationUser> _loginService; public AccountController(ILoginService<ApplicationUser> loginService) { _loginService = loginService; }
把依賴的創建丟給其它人,自己只負責使用,其它人丟給你依賴的這個過程理解為註入。
1.3 為什麽要反轉?
var controller = new AccountController(new EFLoginService()); controller.Login(userName, password); // 用Redis來替換原來的EF登錄 var controller = new AccountController(new RedisLoginService()); controller.Login(userName, password);
1.4 何為容器
二、.NET Core DI
2.1 實例的註冊
var serviceCollection = new ServiceCollection() .AddTransient<ILoginService, EFLoginService>() .AddSingleton<ILoginService, EFLoginService>() .AddScoped<ILoginService, EFLoginService>();
public interface IServiceCollection : IList<ServiceDescriptor> { }
我們上面的AddTransient、AddSignletone和Scoped方法是IServiceCollection的擴展方法, 都是往這個List裏面添加ServiceDescriptor。
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 實例的生命周期之單例
public enum ServiceLifetime { Singleton, Scoped, Transient }
public interface IOperation { Guid OperationId { get; } } public interface IOperationSingleton : IOperation { } public interface IOperationTransient : IOperation{} public interface IOperationScoped : IOperation{}
我們的 Operation實現很簡單,可以在構造函數中傳入一個Guid進行賦值,如果沒有的話則自已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; }
在程序內我們可以多次調用ServiceProvider的GetService方法,獲取到的都是同一個實例。
var services = new ServiceCollection(); // 默認構造 services.AddSingleton<IOperationSingleton, Operation>(); // 自定義傳入Guid空值 services.AddSingleton<IOperationSingleton>(new Operation(Guid.Empty)); // 自定義傳入一個New的Guid services.AddSingleton <IOperationSingleton>(new Operation(Guid.NewGuid())); var provider = services.BuildServiceProvider(); // 輸出singletone1的Guid var singletone1 = provider.GetService<IOperationSingleton>(); Console.WriteLine($"signletone1: {singletone1.OperationId}"); // 輸出singletone2的Guid var singletone2 = provider.GetService<IOperationSingleton>(); Console.WriteLine($"signletone2: {singletone2.OperationId}"); Console.WriteLine($"singletone1 == singletone2 ? : { singletone1 == singletone2 }");
我們對IOperationSingleton註冊了三次,最後獲取兩次,大家要註意到我們獲取到的始終都是我們最後一次註冊的那個給了一個Guid的實例,前面的會被覆蓋。
2.3 實例生命周期之Tranisent
這次我們獲取到的IOperationTransient為兩個不同的實例。
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
var services = new ServiceCollection() .AddScoped<IOperationScoped, Operation>() .AddTransient<IOperationTransient, Operation>() .AddSingleton<IOperationSingleton, Operation>();
接下來我們用ServiceProvider.CreateScope方法創建一個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 },\ntransient1: {transient1.OperationId},\nsingleton1: {singleton1.OperationId}"); Console.WriteLine($"scope2: { scopeobj2.OperationId },\ntransient2: {transient2.OperationId},\nsingleton2: {singleton2.OperationId}"); }
接下來
如果再創建一個新的Scope運行,
大家註意到上面我們一共得到了 4個Transient實例,2個Scope實例,1個Singleton實例。
三、DI在ASP.NET Core中的應用
3.1在Startup類中初始化
ASP.NET Core可以在Startup.cs的 ConfigureService中配置DI,大家看到 IServiceCollection這個參數應該就比較熟悉了。
public void ConfigureServices(IServiceCollection services) { services.AddTransient<ILoginService<ApplicationUser>,EFLoginService>(); services.AddMvc(); )
ASP.NET Core的一些組件已經提供了一些實例的綁定,像AddMvc就是Mvc Middleware在 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 Controller中使用
private ILoginService<ApplicationUser> _loginService; public AccountController(ILoginService<ApplicationUser> loginService) { _loginService = loginService; }
我們只要在控制器的構造函數裏面寫了這個參數,ServiceProvider就會幫我們註入進來。這一步是在Mvc初始化控制器的時候完成的,我們後面再介紹到Mvc的時候會往細裏講。
3.3 View中使用
@using MilkStone.Services; @model MilkStone.Models.AccountViewModel.LoginViewModel @inject ILoginService<ApplicationUser> loginService <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head></head> <body> @loginService.GetUserName() </body> </html>
3.4 通過 HttpContext來獲取實例
HttpContext.RequestServices.GetService<ILoginService<ApplicationUser>>();
四、如何替換其它的Ioc容器
builder.RegisterGeneric(typeof(LoggingBehavior<,>)).As(typeof(IPipelineBehavior<,>)); builder.RegisterGeneric(typeof(ValidatorBehavior<,>)).As(typeof(IPipelineBehavior<,>));
這會給我們的初始化帶來一些便利性,我們來看看如何替換Autofac到ASP.NET Core。我們只需要把Startup類裏面的 ConfigureService的 返回值從 void改為 IServiceProvider即可。而返回的則是一個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 有何變化
【轉載】ASP.NET Core 依賴註入