ASP.NET Core 釋放 IDisposable 對象的四種方法

来源:http://www.cnblogs.com/tdfblog/archive/2017/07/07/four-ways-to-dispose-idisposables-in-asp-net-core.html
-Advertisement-
Play Games

這篇文章介紹了在ASP.NET Core應用程式中可以用於處理釋放資源的一些方法,特別是在使用內置的依賴註入容器時。 ...


本文翻譯自《Four ways to dispose IDisposables in ASP.NET Core》,由於水平有限,故無法保證翻譯完全正確,歡迎指出錯誤。謝謝!

IDisposable 介面是.NET中最常用的介面之一。當類型包含非托管資源的引用,比如視窗句柄、文件或網路通信,可以實現IDisposable介面。垃圾收集器自動釋放托管(即.NET)對象的記憶體,但不知道如何處理非托管資源。通過實現IDisposable介面,您可以在類被釋放時正確地清理這些資源。

這篇文章介紹了在ASP.NET Core應用程式中可以用於處理釋放資源的一些方法,特別是在使用內置的依賴註入容器時。

為了達到這篇文章的目的,我在示例中使用下麵實現了IDisposable介面的類。為了達到我們演示的目的,只需要將日誌輸出到控制台,而不需要做任何實際的清理工作。

public class MyDisposable : IDisposable  
{
    public MyDisposable()
    {
        Console.WriteLine("+ {0} was created", this.GetType().Name);
    }

    public void Dispose()
    {
        Console.WriteLine("- {0} was disposed!", this.GetType().Name);
    }
}

現在來看看我們的方案。

最簡單的方法 - using語法

在代碼中使用using語句塊釋放一個IDisposable對象是一種最普通的方法:

using(var myObject = new MyDisposable())  
{
    // myObject.DoSomething();
}

使用using語句塊的方式,無論是否拋出異常,都能確保Dispose方法可以正常的執行。如果需要,您也可以使用try- finally語句塊的方式:

MyDisposable myObject;  
try  
{
    myObject = new MyDisposable();
    // myObject.DoSomething();
}
finally  
{
    myObject?.Dispose();
}

您會發現通常在使用文件或流(在短暫的某一範圍內)等會用此模式。不幸的是,有時不一定符合這種情況,您可能需要在其它的地方釋放該對象。根據您的真實情況,還可以使用一些其它的方式。

註意: 只要有可能,最好的做法就是將它們在創建的使用範圍內釋放。這將有助於防止應用程式中的記憶體泄漏和意外的文件鎖,或者對象意外地未釋放。


在請求結束時釋放 - 使用RegisterForDispose

當您在ASP.NET Core或任何Web應用程式工作時,將對象的使用範圍限定為單個請求是非常常見的。也就是說,任何您在請求時創建的對像,在請求完成時釋放該對象。

有很多方法可以做到這一點。最常見的方法是在利用依賴容器(我馬上就會講到),但有時候不可能,因為您可能需要在代碼中手動創建IDisposable對象。

如果您手動創建一個IDisposable實例,則可以將該實例註冊到HttpContext中,以便在請求結束時,該實例被自動釋放。只需將實例傳遞給HttpContext.Response.RegisterForDispose方法:

public class HomeController : Controller  
{
    readonly Disposable _disposable;

    public HomeController()
    {
        _disposable = new RegisteredForDispose();
    }

    public IActionResult Index()
    {
        // register the instance so that it is disposed when request ends
        HttpContext.Response.RegisterForDispose(_disposable);
        Console.Writeline("Running index...");
        return View();
    }
}

在這個例子中,我在HomeController的構造函數中創建Disposable對象,然後在action方法中註冊它。這種設計有點做作,但至少展示了這種機制。

如果執行此action方法,您將看到以下內容:

$ dotnet run
Hosting environment: Development  
Content root path: C:\Users\Sock\Repos\RegisterForDispose  
Now listening on: http://localhost:5000  
Application started. Press Ctrl+C to shut down.  
+ MyDisposable was created
Running index...  
- MyDisposable was disposed!

HttpContext負責為我們釋放我們創建的對象!

警告: 我在action方法中註冊實例,而不是構造方法,是因為在構造函數中HttpContextnull

在您的代碼中,RegisterForDispose對處理創建的服務很有用。但是鑒於Dispose模式僅適用於使用非托管資源的類,您可能會發現,通常情況下,您的IDisposable類被封裝在使用依賴容器註冊的服務中。

正如 Mark Rendle 指出的那樣,Controller 本身也將在請求結束時釋放,因此您可以使用該機制來處理您創建的任何對象。


自動釋放服務 - 利用內置依賴容器

ASP.NET Core附帶一個簡單的內置依賴容器,您可以使用“Transient”,“Scoped”或“Singleton”註冊您的服務。你可以在這裡瞭解更多,所以我假設您已經知道如何使用它來註冊您的服務。

請註意,本文僅討論內置容器 - 第三方容器可能有其它關於自動處理服務的規則。

內置容器可以填充任何服務創建的依賴項,它將實現了IDisposable介面的對象,將在適當的時候由容器釋放。因此TransientScoped實例將在請求結束時(或更準確地說,在範圍結束時),Singleton實例在應用程式被關閉釋放,並且ServiceProvider自身也會被釋放。

這意味著只要您不提供具體的實例,提供者將釋放您註冊的任何服務。例如,我將創建一些可釋放類:

public class TransientCreatedByContainer: MyDisposable { }  
public class ScopedCreatedByFactory : MyDisposable { }  
public class SingletonCreatedByContainer: MyDisposable {}  
public class SingletonAddedManually: MyDisposable {}  

Startup.ConfigureServices方法以不同的方式註冊它們。我將這樣註冊:

  • TransientCreatedByContainer - transient
  • ScopedCreatedByFactory - scoped,使用lambda函數作為工廠
  • SingletonCreatedByContainer - singleton
  • SingletonAddedManually - singleton,傳遞具體的實例對象
public void ConfigureServices(IServiceCollection services)  
{
    // other services

    // these will be disposed
    services.AddTransient<TransientCreatedByContainer>();
    services.AddScoped(ctx => new ScopedCreatedByFactory());
    services.AddSingleton<SingletonCreatedByContainer>();

    // this one won't be disposed
    services.AddSingleton(new SingletonAddedManually());
}

最後,我將在HomeController中依次傳每個實例,因此依賴容器將根據需要創建/註入實例:

public class HomeController : Controller  
{
    public HomeController(
        TransientCreatedByContainer transient,
        ScopedCreatedByFactory scoped,
        SingletonCreatedByContainer createdByContainer,
        SingletonAddedManually manually)
    { }

    public IActionResult Index()
    {
        return View();
    }
}

當我運行應用程式,點擊主頁,然後停止應用程式,我將得到以下輸出:

$ dotnet run
+ SingletonAddedManually was created
Content root path: C:\Users\Sock\Repos\RegisterForDispose  
Now listening on: http://localhost:5000  
Application started. Press Ctrl+C to shut down.  
+ TransientCreatedByContainer was created
+ ScopedCreatedByFactory was created
+ SingletonCreatedByContainer was created
- TransientCreatedByContainer was disposed!
- ScopedCreatedByFactory was disposed!
Application is shutting down...  
- SingletonCreatedByContainer was disposed!

這裡有幾件事要註意:

  • SingletonAddedManually 是在Web主機完成設置之前創建的,因此,在日誌開始之前,它將寫入控制台
  • SingletonCreatedByContainer 在我們關閉服務之後被釋放
  • SingletonAddedManually 從來沒有釋放,因為我們提供了一個具體的實例!

請註意,由依賴容器創建的對象被釋放的行為只適用於ASP.NET Core 1.1及更高版本。在ASP.NET Core 1.0中,所有容器註冊的對象都會被釋放。

讓容器幫您處理IDisposable對象顯然很方便,特別是您可能已經在註冊您的服務!這裡唯一的需要註意的是您需要釋放您自己創建的對象。正如我剛纔所說,如果可能,您應該儘量使用using語法,但這並不總是可能的。幸運的是,ASP.NET Core 應用程式的生命周期提供了機制,所以在應用程式關閉時可以進行一些清理。


應用程式結束時釋放 - 利用 IApplicationLifetime 事件

ASP.NET Core公開了一個稱為 IApplicationLifetime 的介面,可用於在應用程式啟動或關閉時執行代碼:

public interface IApplicationLifetime  
{
    CancellationToken ApplicationStarted { get; }
    CancellationToken ApplicationStopping { get; }
    CancellationToken ApplicationStopped { get; }
    void StopApplication();
}

您可以將其註入您的Startup類(或其它類),並註冊您需要的事件。擴展前面的例子,我們在 Startup.csConfigure方法中註入IApplicationLifetimeSingletonAddedManually實例的單例:

public void Configure(  
    IApplicationBuilder app, 
    IApplicationLifetime applicationLifetime,
    SingletonAddedManually toDispose)
{
    applicationLifetime.ApplicationStopping.Register(OnShutdown, toDispose);

    // configure middleware etc
}

private void OnShutdown(object toDispose)  
{
    ((IDisposable)toDispose).Dispose();
}

我創建了一個簡單的幫助方法,傳入SingletonAddedManually的實例,將其轉換為IDisposable並將其釋放。該幫助方法被註冊到類型是CancellationTokenApplicationStopping屬性中,當關閉應用程式時,該方法被觸發。

如果我們再次運行應用程式,通過此額外的註冊,您可以看到該SingletonAddedManually實例現在已被釋放,就在應用程式關閉之後觸發。

$ dotnet run
+ SingletonAddedManually was created
Content root path: C:\Users\Sock\Repos\RegisterForDispose  
Now listening on: http://localhost:5000  
Application started. Press Ctrl+C to shut down.  
+ TransientCreatedByContainer was created
+ ScopedCreatedByFactory was created
+ SingletonCreatedByContainer was created
- TransientCreatedByContainer was disposed!
- ScopedCreatedByFactory was disposed!
Application is shutting down...  
- SingletonAddedManually was disposed!
- SingletonCreatedByContainer was disposed!


概要

您有四種不同的方法來處理您的IDisposable對象。只要有可能,您應該使用using語法,或者讓依賴容器為您釋放對象。對於不可能的情況,ASP.NET Core提供了兩種可以掛接的機制:RegisterForDispose和IApplicationLifetime。

轉載請註明出處,原文鏈接:http://www.cnblogs.com/tdfblog/p/four-ways-to-dispose-idisposables-in-asp-net-core.html


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

-Advertisement-
Play Games
更多相關文章
  • 編程語言範式 常見的編程範式有命令式編程(Imperative programming),函數式編程,邏輯式編程; 許多現存的編程語言都可基於其計算模型加以分類,歸入某些語言族,或者屬於某種編程範式。按照不同的規則,可以有多種分類的方法,而且不同的學者對某些語言的具體歸屬也有不同的意見。 給出一種系 ...
  • 前言Visual Studio 2017已經發佈了很久了。做為集成了Asp.Net Core 1.1的地表最強IDE工具,越來越受.NET系的開發人員追捧。隨著Google Angular4的發佈。我一直在想,怎麼能夠用這個地表最強IDE工具編寫Angular4的Asp.Net Core項目。經過不... ...
  • 或許有人之前在java開發中使用過SQLite,對它有些印象。在用Winform或Wpf開發小應用程式時,發現用SQLite資料庫也是不錯的。就像一個會員管理軟體,開發完畢後,可以省去想sqlserver那些複雜的操作。軟體安裝時,不需要額外的資料庫環境,簡單、便捷。看到這裡,您是否對SQLite感 ...
  • 這個是前幾個月利用業餘時間實現的微信功能,暫時還沒用實現完,只實現了微信臨時聊天人員的顯示,消息數量顯示,聊天視窗記錄顯示,通訊錄人員的顯示,訂閱的查看等,發送功能沒有完成,有興趣的人,自己下載代碼去實現。 轉載地址:http://blog.csdn.net/wanlong360599336/art ...
  • 在某些場景下,比如在對接銀行支付的開發中,會需要手動註冊dll文件,本篇博客就總結下在Dos命令下註冊,取消註冊dll的操作方法 1.註冊dll (1)用管理員身份打開cmd視窗,切換到dll所在目錄 (2)執行Regsvr32 SAFESIGN.dll命令(SAFESIGN.dll為要註冊的dll ...
  • 本文參考自《Step by step: Expose ASP.NET Core over HTTPS with Docker》 自從微軟發佈.net core以來,就在許多社區掀起了討論,筆者也是在工作中開始學習.net core/asp.net core的。說實話,在學習開發asp.net cor ...
  • 老闆給我的第一個硬體就是一個讀卡器, 說讓我做一下試試,於是從網上查了查就寫了出來,相當的簡單。 但是後來還有一個地磅的串口通訊,我整整搞了一天。 在窗體類的構造函數中寫入 Form.CheckForIllegalCrossThreadCalls = false; 可以線上程外更新窗體,這樣就可以一 ...
  • 本文大部分內容摘自 《.NET開發專家·亮劍.NET : .NET深入體驗與實戰精要》 博主只是搬運工,不喜勿噴。 關於虛方法,抽象類這一部分一直不是太清楚,目前的工作中也接觸不到這些。 前幾天下載了一本書,發現寫的很形象,讓我豁然開朗。 整理一下,再打一遍,加深理解,也幫助更多的初學者瞭解這部分知 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...