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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...