在ASP.NET Core中处置IDisposable的四种方法
.NET中最常用的实现接口之一是该IDisposable
接口。IDisposable
当类包含对非托管资源( .aspx)的引用(例如窗口句柄,文件或套接字)时,它们将实现。垃圾收集器会自动释放托管(即.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
语句
当典型的建议的方法消耗的IDisposable
在你的代码,是一个using
块:
using(var myObject = new MyDisposable())
{
// myObject.DoSomething();
}
IDisposable
以这种方式使用s可以确保正确处置它们,无论它们是否引发异常。如有必要,您也可以使用try
-finally
块:
MyDisposable myObject;
try
{
myObject = new MyDisposable();
// myObject.DoSomething();
}
finally
{
myObject?.Dispose();
}
在处理文件或流时,您经常会发现这种模式-您只需要短暂地完成这些工作,并且可以在同一范围内完成这些工作。不幸的是,有时这不适合您的情况,您可能需要从其他地方处置该对象。根据您的实际情况,您还可以使用许多其他选项。
注意:在可能的情况下,最佳做法是将对象放置在与创建对象相同的范围内。这将有助于防止内存泄漏和应用程序中意外的文件锁定,因为这些意外的对象意外处置。
在请求结束时处理-使用 RegisterForDispose

在ASP.NET Core或任何Web应用程序中工作时,将对象范围限定为单个请求是很常见的。也就是说,您创建的用于处理要在请求完成时处理的请求的任何内容。
有很多方法可以做到这一点。最常见的方法是利用我稍后会谈到的DI容器,但是有时这是不可能的,您需要用自己的代码创建一次性容器。
如果您正在手动创建的实例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();
}
}
在此示例中,我正在创建的Disposable
构造函数期间HomeController
,然后在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
需要处置我们的对象对我们的关怀!
警告:我将实例注册在操作方法中,而不是在构造方法中,因为它
HttpContext
可能null
在构造函数中!
RegisterForDispose
new
在代码中建立服务时很有用。但是,鉴于只有使用非托管资源的类才需要使用Dispose,因此您可能会发现,通常情况下,您的IDisposable
类封装在使用DI容器注册的服务中。
正如Mark Rendle所指出的那样,其
Controller
本身也将在请求结束时进行处理,因此您可以使用该机制来处理您创建的任何对象。
自动处理服务-利用内置的DI容器
ASP.NET Core带有一个简单的内置DI容器,您可以将其注册为Transient,Scoped或Singleton服务。您可以在此处阅读有关内容,因此我假设您已经知道如何使用它来注册服务。
请注意,本文仅讨论内置容器-第三方容器在自动处理服务方面可能还有其他规则。
内置容器为填充依赖关系而创建的任何服务(也IDisposable
将实现)都将在适当的位置由容器处理。因此Transient
,Scoped
实例将被放置在请求的末尾(或更准确地说,将在范围的末尾),并且Singleton
服务将在应用程序被拆除并且其ServiceProvider
自身被放置时被放置。
为了明确起见,这意味着只要您不提供特定实例,提供程序就将处置您向其注册的任何服务。例如,我将创建许多一次性类:
public class TransientCreatedByContainer: MyDisposable { }
public class ScopedCreatedByFactory : MyDisposable { }
public class SingletonCreatedByContainer: MyDisposable {}
public class SingletonAddedManually: MyDisposable {}
并以不同的方式在中注册每个Startup.ConfigureServices
。我正在注册
TransientCreatedByContainer
作为暂时的ScopedCreatedByFactory
范围内,使用lambda函数作为工厂SingletonCreatedByContainer
作为一个单身人士SingletonAddedManually
通过传入对象的特定实例作为单例。
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
,因此DI容器将根据需要创建/注入实例:
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
从来没有处置过,因为它是提供给特定实例的!
请注意,仅处理由DI容器创建的对象的行为适用于ASP.NET Core 1.1及更高版本。在ASP.NET Core 1.0中,处理了在容器中注册的所有对象。
让容器IDisposable
为您处理s显然很方便,特别是因为无论如何您可能都在用它注册服务!这里唯一明显的漏洞是您是否需要处置自己创建的对象。正如我最初所说,如果可能的话,您应该支持一个using
声明,但这并不总是可能的。幸运的是,ASP.NET Core提供了应用程序生命周期的挂钩,因此您可以在应用程序关闭时进行一些清理。
在应用程序结束时处理-挂接到IApplicationLifetime
事件
ASP.NET Core公开了一个称为的接口IApplicationLifetime
,该接口可在应用程序启动或关闭时用于执行代码:
public interface IApplicationLifetime
{
CancellationToken ApplicationStarted { get; }
CancellationToken ApplicationStopping { get; }
CancellationToken ApplicationStopped { get; }
void StopApplication();
}
您可以将其注入您的Startup
班级(或其他地方)并注册到所需的事件。扩展前面的示例,我们可以将实例IApplicationLifetime
和SingletonSingletonAddedManually
实例都注入Startup.csConfigure
方法中:
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
,然后进行处理。此帮助程序方法已注册到被CancellationToken
调用的ApplicationStopping
,在关闭应用程序时将触发该方法。
如果我们再次运行该应用程序,并进行了额外的注册,则可以看到该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
语句,或让DI容器为您处理对象。对于无法实现的情况,ASP.NET Core提供了两种可以连接的机制:RegisterForDispose
和IApplicationLifetime
。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构