1. 程式人生 > >循序漸進學.Net Core Web Api開發系列【11】:依賴注入

循序漸進學.Net Core Web Api開發系列【11】:依賴注入

系列目錄

一、概述

本篇介紹如何採用依賴注入的方式建立和使用物件,主要從應用層面進行描述,不涉及具體的內部原理。

二、演練

假設要做一個日誌服務的類,它實現在控制檯打印出帶時間資訊的日誌資訊。

首先定義該服務的介面與實現類。

   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方法。