1. 程式人生 > >如何在ASP.NET Core應用中實現與第三方IoC/DI框架的整合?

如何在ASP.NET Core應用中實現與第三方IoC/DI框架的整合?

我們知道整個ASP.NET Core建立在以ServiceCollection/ServiceProvider為核心的DI框架上,它甚至提供了擴充套件點使我們可以與第三方DI框架進行整合。對此比較瞭解的讀者朋友應該很清楚,針對第三方DI框架的整合可以通過在定義Startup型別的ConfigureServices方法返回一個ServiceProvider來實現。但是真的有這麼簡單嗎?

一、ConfigureServices方法返回的ServiceProvider貌似沒有用!?

我們可以通過一個簡單的例項來說明這個問題。我們先定義瞭如下這個一個MyServiceProvider,它實際上是對另一個ServiceProvider的封裝。簡單起見,我們利用一個字典來儲存服務介面與實現型別的對映關係,這個關係可以通過呼叫Registe方法來註冊。在提供服務例項的GetService方法中,如果提供的服務型別已經被註冊,我們會建立並返回對應的例項物件,否則我們將利用封裝的這個ServiceProvider來提供服務。為了確保服務例項能夠被正常回收,如果服務型別實現了IDisposable介面,我們會將它新增到通過欄位_disposables表示的集合中。當MyServiceProvider的Dispose方法被呼叫的時候,提供的這些服務例項的Dispose方法會被呼叫。

   1: public class MyServiceProvider : IServiceProvider, IDisposable
   2: {
   3:     private IServiceProvider        _innerServiceProvider;
   4:     private Dictionary<Type, Type>  _services;
   5:     private List<IDisposable>       _disposables;
   6:  
   7:     public MyServiceProvider(IServiceProvider innerServiceProvider)
   8:     {
   9:         _innerServiceProvider  = innerServiceProvider;
  10:         this._services         = new Dictionary<Type, Type>();
  11:         _disposables           = new List<IDisposable>();
  12:     }
  13:  
  14:  
  15:     public MyServiceProvider Register<TFrom, TTo>() where
TTo: TFrom, new()
  16:     {
  17:         _services[typeof(TFrom)] = typeof(TTo);
  18:         return this;
  19:     }
  20:  
  21:     public object GetService(Type serviceType)
  22:     {
  23:         Type implementation;
  24:         if (_services.TryGetValue(serviceType, out implementation))
  25:         {
  26:             object service = Activator.CreateInstance(implementation);
  27:             IDisposable disposbale = service as IDisposable;
  28:             if (null != disposbale)
  29:             {
  30:                 _disposables.Add(disposbale);
  31:             }
  32:             return service;
  33:         }
  34:         return _innerServiceProvider.GetService(serviceType);
  35:     }
  36:  
  37:     public void Dispose()
  38:     {
  39:         (_innerServiceProvider as IDisposable)?.Dispose();
  40:         foreach (var it in _disposables)
  41:         {
  42:             it.Dispose();
  43:         }
  44:         _disposables.Clear();
  45:     }
  46: }

我們按照如下的方式在一個ASP.NET Core應用中使用MyServiceProvider。如下面的程式碼片斷中,在註冊的Starup型別中,我們讓ConfigureServices方法返回一個MyServiceProvider物件。服務介面IFoobar和實現型別Foobar之間的對映註冊在這個MyServiceProvider物件上。在處理請求的時候,我們利用當前HttpContext物件的RequestServices屬性得到為請求處理提供服務的ServiceProvider,並試圖利用它得到註冊的IFoobar服務。

   1: public class Program
   2: {
   3:     public static void Main(string[] args)
   4:     {
   5:         new WebHostBuilder()
   6:             .UseKestrel()
   7:             .UseStartup<Startup>()
   8:             .Build()
   9:             .Run();
  10:     }
  11: }
  12:  
  13: public class Startup
  14: {
  15:     public IServiceProvider ConfigureServices(IServiceCollection services)
  16:     {
  17:         return new MyServiceProvider(services.BuildServiceProvider())
  18:             .Register<IFoobar, Foobar>();
  19:     }
  20:  
  21:     public void Configure(IApplicationBuilder app)
  22:     {
  23:         app.UseDeveloperExceptionPage()
  24:             .Run(async context => await context.Response.WriteAsync(context.RequestServices.GetRequiredService<IFoobar>().GetType().Name));
  25:     }
  26: }
  27: public interface IFoobar { }
  28: public class Foobar : IFoobar { }

整個應用就這樣簡單,貌似也沒有什麼問題,但是我們啟動應用並利用瀏覽器訪問該應用是就會出現如下所示的錯誤。錯誤資訊表示服務介面IFoobar尚未被註冊。

image

二、原因何在?

我們明明在返回的ServiceProvider註冊了IFoobar和Foobar之間的對映關係,為什麼RequestServices返回的ServiceProvider說該服務尚未被註冊呢?唯一的解釋就是ConfigureServices方法返回的ServiceProvider與HttpContext的RequestServices返回的ServiceProvider根本就不是同一個。實際上它們本來就不是同一個物件。

我在《從兩個不同的ServiceProvider說起》中曾經談到過:ConfigureServices方法返回的ServiceProvider將會作為WebHost的ServiceProvider,對於每次接收的請求,WebHost會根據這個ServiceProvider建立一個新的ServiceProvider來作為HttpContext的RequestServices屬性,這兩個ServiceProvider具有父子管理。照例說,如果RequestServices返回的ServiceProvider是根據ConfigureServices方法返回的ServiceProvider建立的,那麼它也應該能夠識別註冊的服務型別IFoobar,那麼為什麼依然會出現錯誤呢?

要了解這個問題,就需要知道這個所謂的“子ServiceProvider”是如何被創建出來的,這其中涉及到ServiceScope的概念。簡單來說,ServiceScope是對一個ServiceProvider的封裝,前者決定後者的生命週期。ServiceScope由ServiceScopeFactory建立,後者以一個服務的形式註冊到“父ServiceProvider”上面。當“父ServiceProvider”需要建立“子ServiceProvider”的時候,它會呼叫GetService方法得到這個ServiceScopeFactory物件(採用的服務介面為IServiceScopeFactory),並利用後者建立一個ServiceScope,這個ServiceScope提供的ServiceProvider就是返回的“子ServiceProvider”。

但是對於我們的MyServiceProvider物件來說,當呼叫它的GetService方法試圖獲取ServiceScopeFactory物件的時候,獲取的實際上是被封裝的那個SerivceProvider關聯的ServiceScopeFactory,那麼很自然建立的“子ServiceProvider”也與MyServiceProvider沒有什麼關係。

三、如何解決這個問題?

既然我們知道了問題的根源,我們自然就有了解決方案。解決方案並不複雜,我們只需要MyServiceProvider的GetService方法返回反映其自身服務註冊相關的ServiceScopeFactory。為此我們定義瞭如下一個ServiceScope和對應的ServiceScopeFactory。

   1: internal class ServiceScope : IServiceScope
   2: {
   3:     private MyServiceProvider _serviceProvider;
   4:  
   5:     public ServiceScope(IServiceScope innserServiceScope, Dictionary<Type, Type> services)
   6:     {
   7:         _serviceProvider = new MyServiceProvider(innserServiceScope.ServiceProvider, services);
   8:     }
   9:     public IServiceProvider ServiceProvider
  10:     {
  11:         get { return _serviceProvider; }
  12:     }
  13:  
  14:     public void Dispose()
  15:     {
  16:         _serviceProvider.Dispose();
  17:     }
  18: }
  19:  
  20: internal class ServiceScopeFactory : IServiceScopeFactory
  21: {
  22:     private IServiceScopeFactory _innerServiceFactory;
  23:     private Dictionary<Type, Type> _services;
  24:  
  25:     public ServiceScopeFactory(IServiceScopeFactory innerServiceFactory, Dictionary<Type, Type> services)
  26:     {
  27:         _innerServiceFactory = innerServiceFactory;
  28:         _services = services;
  29:     }
  30:     public IServiceScope CreateScope()
  31:     {
  32:         return new ServiceScope(_innerServiceFactory.CreateScope(), _services);
  33:     }
  34: }

除此之外,我們為MyServiceProvider添加了一個建構函式,GetService方法也針對IServiceScopeFactory添加了相應的程式碼。

   1: public class MyServiceProvider : IServiceProvider, IDisposable
   2: {
   3:     public MyServiceProvider(IServiceProvider innerServiceProvider, Dictionary<Type, Type> services)
   4:     {
   5:         _innerServiceProvider = innerServiceProvider;
   6:         _services = services;
   7:         _disposables = new List<IDisposable>();
            
           

相關推薦

如何在ASP.NET Core應用實現第三方IoC/DI框架整合

我們知道整個ASP.NET Core建立在以ServiceCollection/ServiceProvider為核心的DI框架上,它甚至提供了擴充套件點使我們可以與第三方DI框架進行整合。對此比較瞭解的讀者朋友應該很清楚,針對第三方DI框架的整合可以通過在定義Startup型別的ConfigureServic

用最簡單的方式在ASP.NET Core應用實現認證、登入和登出

在安全領域,認證和授權是兩個重要的主題。認證是安全體系的第一道屏障,是守護整個應用或者服務的第一道大門。當訪問者請求進入的時候,認證體系通過驗證對方的提供憑證確定其真實身份。認證體系只有在證實了訪問者的真實身份的情況下才會允許其進入。ASP.NET Core提供了多種認證方式,它們的實現都基於相同的認證模型。

ASP.NET Core應用如何設定和獲取執行環境相關的資訊?

HostingEnvironment是承載應用當前執行環境的描述,它是對所有實現了IHostingEnvironment介面的所有型別以及對應物件的統稱。如下面的程式碼片段所示,一個HostingEnvironment物件承載的執行環境的描述資訊體現在定義這個介面的6個屬性上。ApplicationName和

ASP.NET Core的快取[1]:如何在一個ASP.NET Core應用使用快取

.NET Core針對快取提供了很好的支援 ,我們不僅可以選擇將資料快取在應用程序自身的記憶體中,還可以採用分散式的形式將快取資料儲存在一個“中心資料庫”中。對於分散式快取,.NET Core提供了針對Redis和SQL Server的原生支援。除了這個獨立的快取系統之外,ASP.NET Core還藉助一箇中

ASP.NET Core應用如何記錄和檢視日誌

日誌記錄不僅對於我們開發的應用,還是對於ASP.NET Core框架功能都是一項非常重要的功能特性。我們知道ASP.NET Core使用的是一個極具擴充套件性的日誌系統,該系統由Logger、LoggerFactory和LoggerProvider這三個核心物件組成。我們可以通過簡單的配置實現對LoggerF

ASP.NET Core MVC 實現中英文切換

  哈嘍..大家好 很久沒有更新了,今天就來一篇最近開發用到的功能,那就是中英文切換,這個實際上也不是高大上,先說一下原理,在.NET Core框架中給我們提供了全球化的類,叫做Localization,其官方的文件地址傳送門。   在我的專案中,我是這樣操作的,你想用別的方式,也可以看文件自己去搞。這個已經

ASP.NET Core 應用使用 Cookie 進行身份認證

## Overview 身份認證是網站最基本的功能,最近因為業務部門的一個需求,需要對一個已經存在很久的小工具網站進行改造,因為在逐步的將一些離散的系統遷移至 .NET Core,所以趁這個機會將這個老的 .NET Framework 4.0 的專案進行升級 老的專案是一個 MVC 的專案並且有外網訪問的

在 Azure WebApps 運行64位 Asp.net Core 應用

需求 正常 mmu www. module .config 正在 external doc 作為微軟下一代的開源的跨平臺的開發框架, Asp.net core 正在吸引越來越多的開發者基於其構建現代 web 應用。 目前, Azure App Service 也實現了對 a

ASP.NET Core應用程序部署至生產環境(CentOS7)

for linux home web 虛擬 direct director block bic 閱讀目錄 環境說明 準備你的ASP.NET Core應用程序 安裝CentOS7 安裝.NET Core SDK for CentOS7。 部署ASP.NET

在 Docker 部署 ASP.NET CORE 應用

post netcore 工作 ros core 指定 們的 本地 body 有了 Docker 之後, 部署起來卻這間非常方便,環境不用搭了, 直接創建一個 microsoft/aspnetcore 的容器, 在本地開發好後, 把內容直接部署到容器中。 下面的命令是把本

Asp.Net Core 2.0實現HttpResponse繁切換

== sed 存儲 中文簡體 選擇 .net tin contains nts 隨筆背景:因為項目中有個簡單的功能是需要實現中文簡體到繁體的切換,數據庫中存儲的源數據都是中文簡體的,為了省事就想著通過HttpHeader的方式來控制Api返回對應的繁體數據。 實現方式:通過

ASP.NET Core MVC 重寫DefaultControllerActivator實現屬性注入

ASP.NET Core中注入方式預設為構造器注入,不支援屬性注入以及其他更高階的注入.參考下面的說明: 但是對於習慣了屬性注入的開發人員來說比較頭疼,為了實現自動注入,需要額外加一個建構函式,還需要把需要提供的服務一一對應,這種操作兼職逼死強迫症.當然官方也給出解決方

ASP.NET Core應用針對靜態檔案請求的處理[2]: 條件請求區間請求

通過呼叫ApplicationBuilder的擴充套件方法UseStaticFiles註冊的StaticFileMiddleware中介軟體幫助我們處理針對檔案的請求。對於StaticFileMiddleware處理請求的邏輯,大部分讀者都應該想得到:它根據請求的地址找到目標檔案的路徑,然後利用註冊的Cont

ASP.NET Core WebApi簡單畫素轉換跟蹤實現

  畫素跟蹤雖然是最早用於跟蹤營銷轉換的方法,但它仍然被廣泛使用,像Facebook這樣的大公司仍然將其視為跟蹤網頁轉換的方法之一。 由於它的簡單性,通過畫素方法的跟蹤轉換仍然被廣泛使用。它不需要任何複雜的客戶端實現,因為它確保它將在幾乎所有可以載入影象的瀏覽器上執行。它由頁面上的一個簡單的

在IIS除錯ASP.NET Core應用程式

IIS中的除錯提供了更平滑的開發過程,無需考慮您的Web伺服器是否正在執行。 我已經使用ASP.NET核心了一段時間,並且總是錯過了Visual Studio中的直接IIS支援。必須記住啟動專案才能啟動IIS Express,這有點令人討厭。在開發軟體時,我們希望實際的除錯和執行過程儘可能自

ASP.NET Core 專案使用 MediatR 實現中介者模式

 一、前言    最近有在看 DDD 的相關資料以及微軟的 eShopOnContainers 這個專案中基於 DDD 的架構設計,在 Ordering 這個示例服務中,可以看到各層之間的程式碼呼叫與我們之前傳統的呼叫方式似乎差異很大,整個專案各個層之間的程式

15.ASP.NET Core 應用程式的靜態檔案中介軟體

在這篇文章中,我將向大家介紹,如何使用中介軟體元件來處理靜態檔案。這篇文章中,我們討論下面幾個問題:在ASP.NET Core中,我們需要把靜態檔案存放在哪裡?在ASP.NET Core中 wwwroot資料夾是啥?怎樣在ASP.NET Core應用程式中,配置靜態檔案中介軟體?UseFileServer中介

將終結點圖新增到你的ASP.NET Core應用程式

在本文中,我將展示如何使用`DfaGraphWriter`服務在ASP.NET Core 3.0應用程式中視覺化你的終結點路由。上面文章我向您演示瞭如何生成一個有向圖([如我上篇文章中所示](https://www.cnblogs.com/yilezhu/p/13301981.html)),可以使用[Grap

ASP.NET Core MVC構建簡單 Web Api

程序 Getting Started在 ASP.NET Core MVC 框架中,ASP.NET 團隊為我們提供了一整套的用於構建一個 Web 中的各種部分所需的套件,那麽有些時候我們只需要做一個簡單的 Web Api 程序怎麽辦呢?在 GitHub 中的 ASP.NET Core MVC 源碼裏面,我

Linux使用Jexus托管Asp.Net Core應用程序

技術 文件目錄 只需要 true 沒有 repr tag 博文 env 第一步 安裝.Net Core環境 安裝 dotnet 環境參見官方網站 https://www.microsoft.com/net/core。 選擇對應的系統版本進行安裝。安裝完成過後 輸入命令查