.NET Core中的一個介面多種實現的依賴註入與動態選擇看這篇就夠了

来源:https://www.cnblogs.com/yilezhu/archive/2019/01/07/10236163.html
-Advertisement-
Play Games

最近有個需求就是一個抽象倉儲層介面方法需要SqlServer以及Oracle兩種實現方式,為了靈活我在依賴註入的時候把這兩種實現都給註入進了依賴註入容器中,但是在服務調用的時候總是獲取到最後註入的那個方法的實現,這時候就在想能不能實現動態的選擇使用哪種實現呢?如果可以的話那麼我只需要在配置文件中進行 ...


最近有個需求就是一個抽象倉儲層介面方法需要SqlServer以及Oracle兩種實現方式,為了靈活我在依賴註入的時候把這兩種實現都給註入進了依賴註入容器中,但是在服務調用的時候總是獲取到最後註入的那個方法的實現,這時候就在想能不能實現動態的選擇使用哪種實現呢?如果可以的話那麼我只需要在配置文件中進行相應的配置即可獲取到正確的實現方法的調用,這樣的話豈不快哉!今天我們就來一起探討下實現這種需求的幾種實現方式吧。

作者:依樂祝
原文地址:https://www.cnblogs.com/yilezhu/p/10236163.html

代碼演示

在開始實現的方式之前,我們先模擬下代碼。由於真實系統的結構比較複雜,所以這裡我就單獨建一個類似的項目結構代碼。項目如下圖所示:

1546866490439

接下來我來詳細說下上面的結果作用及代碼。

  1. MultiImpDemo.I 這個項目是介面項目,裡面有一個簡單的介面定義ISayHello,代碼如下:

        public interface ISayHello
        {
            string Talk();
        }

    很簡單,就一個模擬講話的方法。

  2. MultiImpDemo.A 這個類庫項目是介面的一種實現方式,裡面有一個SayHello類用來實現ISayHello介面,代碼如下:

    /**
    *┌──────────────────────────────────────────────────────────────┐
    *│ 描    述:                                                    
    *│ 作    者:yilezhu                                             
    *│ 版    本:1.0                                                 
    *│ 創建時間:2019/1/7 17:41:33                             
    *└──────────────────────────────────────────────────────────────┘
    *┌──────────────────────────────────────────────────────────────┐
    *│ 命名空間: MultiImpDemo.A                                   
    *│ 類    名: SayHello                                      
    *└──────────────────────────────────────────────────────────────┘
    */
    using MultiImpDemo.I;
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace MultiImpDemo.A
    {
        public class SayHello : ISayHello
        {
            public string Talk()
            {
                return "Talk from A.SayHello";
            }
        }
    }
  3. MultiImpDemo.B 這個類庫項目是介面的另一種實現方式,裡面也有一個SayHello類用來實現ISayHello介面,代碼如下:

    /**
    *┌──────────────────────────────────────────────────────────────┐
    *│ 描    述:                                                    
    *│ 作    者:yilezhu                                             
    *│ 版    本:1.0                                                 
    *│ 創建時間:2019/1/7 17:41:45                             
    *└──────────────────────────────────────────────────────────────┘
    *┌──────────────────────────────────────────────────────────────┐
    *│ 命名空間: MultiImpDemo.B                                   
    *│ 類    名: SayHello                                      
    *└──────────────────────────────────────────────────────────────┘
    */
    using MultiImpDemo.I;
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace MultiImpDemo.B
    {
        public class SayHello:ISayHello
        {
            public string Talk()
            {
                return "Talk from B.SayHello";
            }
        }
    }
    
  4. MultiImpDemo.Show 這個就是用來顯示我們模擬效果的API項目,首選我們在ConfigureServices中加入如下的代碼來進行上述兩種實現方式的註入:

     services.AddTransient<ISayHello, MultiImpDemo.A.SayHello>();
     services.AddTransient<ISayHello, MultiImpDemo.B.SayHello>();
  5. 在api實現裡面獲取服務併進行模擬調用:

      private readonly ISayHello sayHello;
    
            public ValuesController(ISayHello sayHello)
            {
                this.sayHello = sayHello;
            }
    
            // GET api/values
            [HttpGet]
            public ActionResult<IEnumerable<string>> Get()
            {
                return new string[] { sayHello.Talk() };
            }
    

    代碼很簡單對不對?你應該看的懂吧,這時候我們運行起來項目,然後訪問API'api/values'這個介面,結果總是顯示如下的結果:

    1546867091226

兩種需求對應兩種實現

這裡有兩種業務需求!第一種業務中只需要對其中一種實現方式進行調用,如:業務需要SqlServer資料庫的實現就行了。第二種是業務中對這兩種實現方式都有用到,如:業務急需要用到Oracle的資料庫實現同時也有用到SqlServer的資料庫實現,需要同時往這兩個資料庫中插入相同的數據。下麵分別對這兩種需求進行解決。

業務中對這兩種實現方式都有用到

針對這種情況有如下兩種實現方式:

  1. 第二種實現方式

    其實,在ASP.NET Core中,當你對一個介面註冊了多個實現的時候,構造函數是可以註入一個該介面集合的,這個集合里是所有註冊過的實現。

    下麵我們先改造下ConfigureServices,分別註入下這兩種實現

    services.AddTransient<ISayHello, A.SayHello>();
    services.AddTransient<ISayHello,B.SayHello>();

    接著繼續改造下註入的方式,這裡我們直接註入IEnumerable<ISayHello>如下代碼所示:

    private readonly ISayHello sayHelloA;
            private readonly ISayHello sayHelloB;
            public ValuesController(IEnumerable<ISayHello> sayHellos)
            {
                sayHelloA = sayHellos.FirstOrDefault(h => h.GetType().Namespace == "MultiImpDemo.A");
                sayHelloB = sayHellos.FirstOrDefault(h => h.GetType().Namespace == "MultiImpDemo.B");
            }
    
    
            // GET api/values
            [HttpGet]
            public ActionResult<IEnumerable<string>> Get()
            {
                return new string[] { sayHelloA.Talk() , sayHelloB.Talk()};
            } 
    

    然後運行起來看下效果吧

    1546870734607

  2. 利用AddTransient的擴展方法public static IServiceCollection AddTransient<TService>(this IServiceCollection services, Func<IServiceProvider, TService> implementationFactory) where TService : class; 然後根據我們的配置的實現來進行服務實現的獲取。下麵就讓我們利用代碼來實現一番吧:

      services.AddTransient<A.SayHello>();
                services.AddTransient<B.SayHello>();
    
                services.AddTransient(implementationFactory =>
                {
                    Func<string, ISayHello> accesor = key =>
                    {
                        if (key.Equals("MultiImpDemo.A"))
                        {
                            return implementationFactory.GetService<A.SayHello>();
                        }
                        else if (key.Equals("MultiImpDemo.B"))
                        {
                            return implementationFactory.GetService<B.SayHello>();
                        }
                        else
                        {
                            throw new ArgumentException($"Not Support key : {key}");
                        }
                    };
                    return accesor;
                });
    

    當然了,既然用到了我們配置文件中的代碼,因此我們需要設置下這個配置:

    然後我們具體調用的依賴註入的方式需要變化一下:

    private readonly ISayHello sayHelloA;
            private readonly ISayHello sayHelloB;
    
            private readonly Func<string, ISayHello> _serviceAccessor;
    
            public ValuesController(Func<string, ISayHello> serviceAccessor)
            {
                this._serviceAccessor = serviceAccessor;
    
                sayHelloA = _serviceAccessor("MultiImpDemoA");
                sayHelloB = _serviceAccessor("MultiImpDemoB");
            }
    
    
            // GET api/values
            [HttpGet]
            public ActionResult<IEnumerable<string>> Get()
            {
                return new string[] { sayHelloA.Talk() , sayHelloB.Talk()};
            }

    然後運行看下效果吧:

    1546869793187

    可以看到A跟B的實現都獲取到了!效果實現!

業務只需要對其中一種實現方式的調用

這時候我們可以根據我們預設的配置來動態獲取我們所需要的實現。這段話說的我自己都感覺拗口。話不多少,開魯吧!這裡我將介紹三種實現方式。

  1. 根據我們的配置文件中設置的key來進行動態的註入。

    這種方式實現之前首先得進行相應的配置,如下所示:

      "CommonSettings": {
        "ImplementAssembly": "MultiImpDemo.A"
      }

    然後在註入的時候根據配置進行動態的進行註入:

     services.AddTransient<ISayHello, A.SayHello>();
                services.AddTransient<ISayHello, B.SayHello>();

    然後在服務調用的時候稍作修改:

      private readonly ISayHello sayHello;
            public ValuesController(IEnumerable<ISayHello> sayHellos,IConfiguration configuration)
            {
                sayHello = sayHellos.FirstOrDefault(h => h.GetType().Namespace == configuration.GetSection("CommonSettings:ImplementAssembly").Value);
            }
    
    
            // GET api/values
            [HttpGet]
            public ActionResult<IEnumerable<string>> Get()
            {
                return new string[] { sayHello.Talk() };
            }

    OK,到這裡運行一下看下效果吧!然後改下配置文件再看下效果!

    1546871452531

  2. 第二種實現方式,即介面參數的方式這樣可以避免上個方法中反射所帶來的性能損耗。

    這裡我們改造下介面,介面中加入一個程式集的屬性,如下所示:

    public interface ISayHello
        {
            string ImplementAssemblyName { get; }
            string Talk();
        }

    對應的A跟B中的實現代碼也要少做調整:

    A:

     public string ImplementAssemblyName => "MultiImpDemo.A";
    
            public string Talk()
            {
                return "Talk from A.SayHello";
            }

    B:

     public string ImplementAssemblyName => "MultiImpDemo.B";
    
            public string Talk()
            {
                return "Talk from B.SayHello";
            }

    然後,在實現方法調用的時候稍微修改下:

     private readonly ISayHello sayHello;
            public ValuesController(IEnumerable<ISayHello> sayHellos,IConfiguration configuration)
            {
                sayHello = sayHellos.FirstOrDefault(h => h.ImplementAssemblyName == configuration.GetSection("CommonSettings:ImplementAssembly").Value);
            }
    
    
            // GET api/values
            [HttpGet]
            public ActionResult<IEnumerable<string>> Get()
            {
                return new string[] { sayHello.Talk() };
            }
    

    效果自己運行下看下吧!

  3. 第三種實現是根據配置進行動態的註冊

    首先修改下ConfigureServices方法:

     var implementAssembly = Configuration.GetSection("CommonSettings:ImplementAssembly").Value;
                if (string.IsNullOrWhiteSpace(implementAssembly)) throw new ArgumentNullException("CommonSettings:ImplementAssembly未配置");
                if (implementAssembly.Equals("MultiImpDemo.A"))
                {
                    services.AddTransient<ISayHello, A.SayHello>();
    
                }
                else
                {
                    services.AddTransient<ISayHello, B.SayHello>();
    
                }

    這樣的話就會根據我們的配置文件來進行動態的註冊,然後我們像往常一樣進行服務的調取即可:

      private readonly ISayHello _sayHello;
            public ValuesController(ISayHello sayHello)
            {
                _sayHello = sayHello;
            }
    
    
            // GET api/values
            [HttpGet]
            public ActionResult<IEnumerable<string>> Get()
            {
                return new string[] { _sayHello.Talk() };
            }

    運行即可得到我們想要的效果!

總結

本文從具體的業務需求入手,根據需求來或動態的進行對應服務的獲取,或同時使用兩個不同的實現!希望對您有所幫助!如果您有更多的實現方法可以在下方留言,或者加入.NET Core實戰千人群跟637326624大伙進行交流,最後感謝您的閱讀!


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

-Advertisement-
Play Games
更多相關文章
  • 前言: 由於導師在我的畢設項目裡加了消息系統(本來想水水就過的..),沒辦法...來稍微研究研究吧..簡單簡單... 需求分析 我的畢設是一個博客系統 ,類似於簡書這樣的,所以消息系統也類似,在用戶的消息里包含了有:喜歡和贊、評論、關註、私信這樣的一類東西,這樣的一個系統應該包含以下的功能: 1. ...
  • 由於 Apache公司發現myBatis的分頁弊端,所以又研發出得補丁:PageHelper 中央倉庫5.1.2版連接地址: <!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper --><dependency ...
  • 變數 什麼是變數 變數就是變化的量,變就是變化,量用於衡量描述對象的狀態 為什麼要有變數 程式執行的本質就是一系列狀態的變化,變是程式執行的直接體現,所以我們需要有一種機制能夠反映或者說是保存下來程式執行時狀態以及狀態的變化。 變數的定義規範 #1.變數名只能是字母、數字或下劃線的組合 #2.變數名 ...
  • 寫在前面 從今天開始的幾篇文章,我將就國內目前比較主流的一些線上學習平臺數據進行抓取,如果時間充足的情況下,會對他們進行一些簡單的分析,好了,平臺大概有 ,`CSDN學院 網易雲課堂 慕課網 mongodb`裡面,如果對上述平臺造成了困擾,請見諒,畢竟我就抓取那麼一小會的時間,不會對伺服器有任何影響 ...
  • Django 系列博客(三) 前言 本篇博客介紹 django 的前後端交互及如何處理 get 請求和 post 請求。 get 請求 get請求是單純的請求一個頁面資源,一般不建議進行賬號信息的傳輸。 配置路由 配置視圖 配置頁面資源 post 請求 配置路由 配置視圖 配置頁面資源 前後端交互 ...
  • Kubernetes簡介 Kubernetes是Google基於Borg開源的容器編排調度引擎,作為CNCF(Cloud Native Computing Foundation)最重要的組件之一,它的目標不僅僅是一個編排系統,而是提供一個規範,可以讓你來描述集群的架構,定義服務的最終狀態,Kuber ...
  • 本文通過一張圖來看一下路由的配置以及請求處理的機制。(ASP.NET Core 系列目錄) 一、概述 路由主要有兩個主要功能: 這兩個功能看起來這兩個是相反的。 A.路由的配置 路由的兩個功能都離不開一個基本的操作:路由的基本配置。在Startup中預設通過 routes.MapRoute(name ...
  • 接下來的幾篇,我想討論下遷移桌面程式到MS Store,可以採用的比較常見、通用性比較強的實施步驟和分層架構。 通常商業項目一般都是不斷的迭代,不太可能突然停止更新現有的桌面版本,然後花很長時間從頭來寫UWP版本,所以我建議將遷移的過程分階段,通過幾個版本的迭代,慢慢地從傳統桌面程式,轉變成一個可以 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...