ASP.NET Core - 依賴註入(四)

来源:https://www.cnblogs.com/wewant/archive/2023/03/01/17110720.html
-Advertisement-
Play Games

4. ASP.NET Core預設服務 之前講了中間件,實際上一個中間件要正常進行工作,通常需要許多的服務配合進行,而中間件中的服務自然也是通過 Ioc 容器進行註冊和註入的。前面也講到,按照約定中間件的封裝一般會提供一個 User{Middleware} 的擴展方法給用戶使用,而服務註冊中也有一個 ...


4. ASP.NET Core預設服務

之前講了中間件,實際上一個中間件要正常進行工作,通常需要許多的服務配合進行,而中間件中的服務自然也是通過 Ioc 容器進行註冊和註入的。前面也講到,按照約定中間件的封裝一般會提供一個 User{Middleware} 的擴展方法給用戶使用,而服務註冊中也有一個類似的約定,一般會有一個 Add{Services} 的擴展方法。

例如一個WebApi項目中,對於控制器路由終結點中間件的配置使用:

builder.Services.AddControllers();

var app = builder.Build();
app.MapControllers();

這也是我們在日常開發中可以學習的方式,隨著業務增長,需要依賴註入的服務也越來越多,我們可以根據業務模塊,通過擴展方法講相應模塊的服務註入註冊進行封裝,命名為 Add{Services},更加清晰明瞭地對我們的業務進行封裝。

.NET Core 框架下預設提供250個以上的的服務,包括 ASP.NET Core MVC、EF Core 等等,當然這些服務很多不會預設就註入到容器中,我們在新建一個項目的時候,不同項目框架的模板會幫我們預設配置好一些最基本的必須的服務,其他的服務我們可以根據自己的需要進行使用。

image

5. 依賴註入配置變形

隨著業務的增長,我們項目工作中的類型、服務越來越多,而每一個服務的依賴註入關係都需要在入口文件通過Service.Add{}方法去進行註冊,這將是非常麻煩的,入口文件需要頻繁改動,而且代碼組織管理也會變得麻煩,非常不優雅。

在許多框架中會對這種通過 Service.Add{xxx} 的方式在代碼中顯式註冊依賴註入關係的方式進行變形,有的可以通過配置文件進行註冊,例如 Java Spring 框架就有這樣大量的配置文件,有的可以通過介面進行預設註冊,有的通過特性進行預設註冊。

這裡稍微簡單介紹一下依賴註入預設註冊的原理,其實也就是通過放射的一些手段,再加上一些約定好的規則而已。

首先需要三個生命周期介面,如下,這三個介面沒有內容,僅僅只是作為標記而已。

public interface ISingleton
{
}
public interface IScoped
{
}
public interface ITransient
{
}

之後需要一個擴展方法,如下:

namespace Microsoft.Extensions.DependencyInjection
{
    public static class ServiceCollectionDependencyExtensions
    {
        public static IServiceCollection AddAutoInject<T>(this IServiceCollection services)
        {
            var register = new ServiceRegister();
            register.AddAssembly(services, typeof(T).Assembly);
            return services;
        }
    }
}

這個擴展方法中調用了註冊器,往容器中註入服務,實現如下:

public class ServiceRegister
{
    public void AddAssembly(IServiceCollection services, Assembly assembly)
    {
        // 查找程式中的類型
        var types = assembly.GetTypes().Where(t => t != null && t.IsClass && !t.IsAbstract && !t.IsGenericType);
        // 遍歷每一個類檢查釋放滿足約定的規則
        foreach (var type in types)
        {
            AddType(services, type);
        }
    }

    /// <summary>
    /// 添加當前類型的依賴註入關係
    /// </summary>
    /// <param name="services"></param>
    /// <param name="type"></param>
    public void AddType(IServiceCollection services, Type type)
    {
        var lifetime = GetLifetimeOrNull(type);
        if (lifetime == null)
        {
            return;
      
        var exposeServices = ExposeService(type);
        foreach (var serviceType in exposeServices)
        {
            var serviceDescriptor = new ServiceDescriptor(serviceType, type, lifetime.Value);
            services.Add(serviceDescriptor);
        }
    }

    /// <summary>
    /// 根據標記介面確定生命周期,如果沒有添加標記介面的,則不會被自動註冊到容器
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    public ServiceLifetime? GetLifetimeOrNull(Type type)
    {
        if (typeof(ISingleton).IsAssignableFrom(type))
        {
            return ServiceLifetime.Singleton;
      	}
        if(typeof(IScoped).IsAssignableFrom(type))
        {
            return ServiceLifetime.Scoped;
      	}
        if(typeof(ITransient).IsAssignableFrom(type))
        {
            return ServiceLifetime.Transient;
      	}
        return null;
    }
    /// <summary>
    /// 根據約定的規則查找當前類對於的服務類型
    /// 通過介面實現的方式,查找當前類實現的介面,如果一個介面名稱去除了 "I" 之後與當前類的後半段一樣,
    /// 則當前類應該被註冊為這個介面的服務。
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    public IList<Type> ExposeService(Type type)
    {
        var serviceTypes = new List<Type>();
        var interfaces = type.GetInterfaces();
        foreach (var interfacesType in interfaces)
        {
            var interfaceName = interfacesType.Name;
            if (interfaceName.StartsWith("I"))
            {
                interfaceName = interfaceName.Substring(1);
          	}
            if (type.Name.EndsWith(interfaceName))
            {
                serviceTypes.Add(interfacesType);
            }
        }
        return serviceTypes;
    }
}

整體的邏輯就是查找遍歷程式集中的所有類型,並通過判別類型是否實現之前定好的三個生命周期介面,從而確定類型是否需要自動註冊到容器中,如果需要再根據約定好的規則獲取需要註冊的服務類型,並且構建服務描述器,再將其添加到容器中。

之後在入口文件中這樣使用:

builder.Services.AddAutoInject<Program>();

而需要自動註入的服務只要多實現一個標記介面即可:

public class Rabbit : IRabbit, ITransient
{
}

以上主要介紹一下依賴註入自動化註冊的思路和基本實現,代碼只是一個基本的演示,比較簡單,很多細節也沒有在這裡體現,但是核心的思路是和ABP框架中的自動註入的方式一樣的,有興趣詳細瞭解一下ABP中的依賴註入的機制的童鞋,可以看一下我其他的文章: ABP 依賴註入(1)



參考文章:
ASP.NET Core 依賴註入 | Microsoft Learn
理解ASP.NET Core - 依賴註入(Dependency Injection)



ASP.NET Core 系列:

目錄:ASP.NET Core 系列總結
上一篇:ASP.NET Core - 依賴註入(三)


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、什麼是繼承 繼承是一種新建類的方式,新建的類稱為子類,被繼承的類稱為父類 繼承的特性是:子類會遺傳父類的屬性 繼承是類與類之間的關係 二、為什麼用繼承 使用繼承可以減少代碼的冗餘 三、對象的繼承 Python中支持一個類同時繼承多個父類 class Parent1: pass class Par ...
  • Synchronized鎖優化 jdk1.6對鎖的實現引入了大量的優化,如自旋鎖、適應性自旋鎖、鎖消除、鎖粗化、偏向鎖、輕量級鎖等技術來減少鎖操作的開銷。 鎖主要存在四中狀態,依次是:無鎖-> 偏向鎖 -> 輕量級鎖 -> 重量級鎖,他們會隨著競爭的激烈而逐漸升級。註意鎖可以升級不可降級,這種策略是 ...
  • 昨晚回家,表弟在看LOL直播,看得我氣不打一處來,差點就想錘他。 身為程式員的表弟,看直播發彈幕居然還在手動發,當時我就用Python寫了一個自動發送彈幕的腳本送給他用。 好了話不多說,我們直接開搞! 先看看效果 名字我就打碼了,當然名字不是關鍵,我直接截圖展示算了,GIF的話,太麻煩了。 接下來我 ...
  • Adobe Lightroom 鍵盤快捷鍵 Adobe Lightroom CC 中 251 個鍵盤快捷鍵的可視化備忘單鍵盤快捷鍵,為開發人員分享快速參考備忘單。 開發速查表大綱 鍵盤快捷鍵 使用面板 導航模塊 使用輔助視窗 在開發模塊中工作 更改視圖和屏幕模式 管理照片和目錄 比較圖庫模塊中的照片 ...
  • 聲明 本文章中所有內容僅供學習交流,抓包內容、敏感網址、數據介面均已做脫敏處理,嚴禁用於商業用途和非法用途,否則由此產生的一切後果均與作者無關,若有侵權,請聯繫我立即刪除! 本文章未經許可禁止轉載,禁止任何修改後二次傳播,擅自使用本文講解的技術而導致的任何意外,作者均不負責,若有侵權,請在公眾號【K ...
  • Integer使用==比較的問題 new一個對象 public Integer(int value) { this.value = value; } 自動裝箱 public static Integer valueOf(int i) { if (i >= IntegerCache.low && i ...
  • 最近在忙於 Fireasy 的重構,3.x 拋棄了 .Net Framework 時代的一些思想和模式,緊密擁抱 .Net Core,但它的思想仍然是開放性和靈活性。今天我主要來說說依賴註入與服務發現。 .Net Core 有自己的一套依賴註入,它的容器暴露給 IServiceCollection, ...
  • 上一章我們對XAML有個初步的認識了,知道XAML是用來設計UI的,那麼說怎麼設計,基本用法和語法分別是什麼呢?接下來我們就系統的簡單學習一下XAML的一些基本語法吧。 1 - XAML的結構 如果學習過Winform或者其他桌面設計的應該知道我們最終設計的是與人員交互的圖形界面。比如在Winfor ...
一周排行
    -Advertisement-
    Play Games
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...