本篇介紹如何採用依賴註入的方式創建和使用對象,主要從應用層面進行描述,不涉及具體的內部原理。 ...
系列目錄
本系列涉及到的源碼下載地址:https://github.com/seabluescn/Blog_WebApi
一、概述
本篇介紹如何採用依賴註入的方式創建和使用對象,主要從應用層面進行描述,不涉及具體的內部原理。
二、演練
假設要做一個日誌服務的類,它實現在控制台列印出帶時間信息的日誌信息。
首先定義該服務的介面與實現類。
public interface ILogService { void LogInfomation(string info); } public class MyLogService : ILogService { void ILogService.LogInfomation(string info) { Console.WriteLine($" ==> MyLogService : {DateTime.Now.ToString()}:{info}"); } }
註冊該服務
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddCors(); services.AddSingleton<ILogService, MyLogService>(); }
註冊成功,我們在Controller中使用該服務:
public class ArticleController : Controller {
private readonly ILogService _myLog; public ArticleController(ILogService myLog) { _myLog = myLog; } [HttpGet("logger")] public void TestLogger(string logger) { _myLog.LogInfomation("hahaha"); return; } }
簡單分析一下:
1、首先通過services.AddSingleton方法向依賴註入容器登記註冊MyLogService;
2、在構建Controller時,根據其構造函數類型遍歷其輸入參數,在依賴註入容器中找到該對象並作為實參傳遞給構造方法。
三、生命周期問題
註冊一個服務,根據生命周期需要的不同,有下麵三種方式:
services.AddSingleton<ILogService, MyLogService>(); services.AddScoped<ILogService, MyLogService>(); ervices.AddTransient<ILogService, MyLogService>();
三種註冊方式分別對應三種生命周期
1)Singleton:單例服務,從當前服務容器中獲取這個類型的實例永遠是同一個實例;
2)Scoped:每個作用域生成周期內創建一個實例;
3)Transient:每一次請求服務都創建一個新實例;
對我們的日誌進行改造,讓其在構建時生成一個ID,通過觀察其guid變化可以理解這三種生命周期的區別。
public class MyLogService : ILogService { public Guid _guid; public MyLogService() { _guid = Guid.NewGuid(); } void ILogService.LogInfomation(string info) { Console.WriteLine($" ==> MyLogService : My Guid is :{_guid}"); Console.WriteLine($" ==> MyLogService : {DateTime.Now.ToString()}:{info}"); } }
四、通過擴展方法註冊服務
通過對IServiceCollection增加擴展方法來註冊服務
public static class MyLogServiceCollectionExtensions { public static void AddMyLog(this IServiceCollection services) { services.AddSingleton<ILogService, MyLogService>(); } }
這樣,使用者的註冊代碼可以修改為:
public void ConfigureServices(IServiceCollection services) {
services.AddMvc();
services.AddCors();
services.AddMyLog();
}
可見AddMvc、AddCors等也是向容器註入服務。
public static IMvcBuilder AddMvc(this IServiceCollection services) { if (services == null) { throw new ArgumentNullException("services"); } IMvcCoreBuilder mvcCoreBuilder = MvcCoreServiceCollectionExtensions.AddMvcCore(services); MvcApiExplorerMvcCoreBuilderExtensions.AddApiExplorer(mvcCoreBuilder); MvcCoreMvcCoreBuilderExtensions.AddAuthorization(mvcCoreBuilder); MvcServiceCollectionExtensions.AddDefaultFrameworkParts(mvcCoreBuilder.PartManager); MvcCoreMvcCoreBuilderExtensions.AddFormatterMappings(mvcCoreBuilder); MvcViewFeaturesMvcCoreBuilderExtensions.AddViews(mvcCoreBuilder); MvcRazorMvcCoreBuilderExtensions.AddRazorViewEngine(mvcCoreBuilder); MvcRazorPagesMvcCoreBuilderExtensions.AddRazorPages(mvcCoreBuilder); TagHelperServicesExtensions.AddCacheTagHelper(mvcCoreBuilder); MvcDataAnnotationsMvcCoreBuilderExtensions.AddDataAnnotations(mvcCoreBuilder); MvcJsonMvcCoreBuilderExtensions.AddJsonFormatters(mvcCoreBuilder); MvcCorsMvcCoreBuilderExtensions.AddCors(mvcCoreBuilder); return new MvcBuilder(mvcCoreBuilder.Services, mvcCoreBuilder.PartManager); }View Code
五、幾個問題
1、如果多次註冊會怎樣
可以多次註冊同一種生命周期的類,如下是可以的:
services.AddSingleton<ILogService, MyLogService>(); services.AddSingleton<ILogService, MyLogService>(); services.AddSingleton<ILogService, MyLogService>();
但下麵這個代碼不行:
services.AddSingleton<ILogService, MyLogService>();
services.AddScoped<ILogService, MyLogService>();
2、如何獲取已經註冊的服務列表
通過ServicesProvider可以獲取服務列表
services.AddMyLog(); services.AddMyLog(); services.AddMyLog(); var provider = services.BuildServiceProvider(); var servicesList = provider.GetServices< ILogService >(); foreach (var service in servicesList) { Console.WriteLine("service:" + service.ToString()); }
以上代碼輸出3條記錄。
但下麵的代碼只輸出一條記錄:
services.AddCors(); services.AddCors(); services.AddCors(); var provider = services.BuildServiceProvider(); var servicesList = provider.GetServices<ICorsService>(); foreach (var service in servicesList) { Console.WriteLine("service:" + service.ToString()); }
具體原因看一下源碼就清楚了:
public static IServiceCollection AddCors(this IServiceCollection services) { if (services == null) { throw new ArgumentNullException("services"); } services.TryAdd(ServiceDescriptor.Transient<ICorsService, CorsService>()); return services; } public static void TryAdd(this IServiceCollection collection, ServiceDescriptor descriptor) { if (!collection.Any((ServiceDescriptor d) => d.ServiceType == descriptor.ServiceType)) { collection.Add(descriptor); } }
所以我們應該按照這個方法修改我們的AddMyLog方法。