ASP.NET Core 释放 IDisposable 对象的四种方法
2017-07-07 09:47
846 查看
本文翻译自《Four ways to dispose IDisposables in ASP.NET Core》,由于水平有限,故无法保证翻译完全正确,欢迎指出错误。谢谢!
这篇文章介绍了在ASP.NET Core应用程序中可以用于处理释放资源的一些方法,特别是在使用内置的依赖注入容器时。
为了达到这篇文章的目的,我在示例中使用下面实现了
现在来看看我们的方案。
使用
您会发现通常在使用文件或流(在短暂的某一范围内)等会用此模式。不幸的是,有时不一定符合这种情况,您可能需要在其它的地方释放该对象。根据您的真实情况,还可以使用一些其它的方式。
注意: 只要有可能,最好的做法就是将它们在创建的使用范围内释放。这将有助于防止应用程序中的内存泄漏和意外的文件锁,或者对象意外地未释放。
在请求结束时释放 - 使用
当您在ASP.NET Core或任何Web应用程序工作时,将对象的使用范围限定为单个请求是非常常见的。也就是说,任何您在请求时创建的对像,在请求完成时释放该对象。
有很多方法可以做到这一点。最常见的方法是在利用依赖容器(我马上就会讲到),但有时候不可能,因为您可能需要在代码中手动创建
如果您手动创建一个
在这个例子中,我在
如果执行此action方法,您将看到以下内容:
警告: 我在action方法中注册实例,而不是构造方法,是因为在构造函数中
在您的代码中,
请注意,本文仅讨论内置容器 - 第三方容器可能有其它关于自动处理服务的规则。
内置容器可以填充任何服务创建的依赖项,它将实现了
这意味着只要您不提供具体的实例,提供者将释放您注册的任何服务。例如,我将创建一些可释放类:
在
最后,我将在
当我运行应用程序,点击主页,然后停止应用程序,我将得到以下输出:
这里有几件事要注意:
请注意,由依赖容器创建的对象被释放的行为只适用于ASP.NET Core 1.1及更高版本。在ASP.NET Core 1.0中,所有容器注册的对象都会被释放。
让容器帮您处理
应用程序结束时释放 - 利用
ASP.NET Core公开了一个称为
您可以将其注入您的
我创建了一个简单的帮助方法,传入
如果我们再次运行应用程序,通过此额外的注册,您可以看到该
转载请注明出处,原文链接:http://www.cnblogs.com/tdfblog/p/four-ways-to-dispose-idisposables-in-asp-net-core.html。
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 = null; 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方法中注册实例,而不是构造方法,是因为在构造函数中
HttpContext为
null!
在您的代码中,
RegisterForDispose对处理创建的服务很有用。但是鉴于Dispose模式仅适用于使用非托管资源的类,您可能会发现,通常情况下,您的
IDisposable类被封装在使用依赖容器注册的服务中。
自动释放服务 - 利用内置依赖容器
ASP.NET Core附带一个简单的内置依赖容器,您可以使用“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- 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.cs 的
Configure方法中注入
IApplicationLifetime和
SingletonAddedManually实例的单例:
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语法,或者让依赖容器为您释放对象。对于不可能的情况,ASP.NET Core提供了两种可以挂接的机制:RegisterForDispose和IApplicationLifetime。
转载请注明出处,原文链接:http://www.cnblogs.com/tdfblog/p/four-ways-to-dispose-idisposables-in-asp-net-core.html。
相关文章推荐
- C#编译器优化那点事 c# 如果一个对象的值为null,那么它调用扩展方法时为甚么不报错 webAPI 控制器(Controller)太多怎么办? .NET MVC项目设置包含Areas中的页面为默认启动页 (五)Net Core使用静态文件 学习ASP.NET Core Razor 编程系列八——并发处理
- 百度地图和高德地图坐标系的互相转换 四种Sandcastle方法生成c#.net帮助类帮助文档 文档API生成神器SandCastle使用心得 ASP.NET Core
- ASP.NET中Server对象的方法
- ASP.NET页面间传递对象的另类方法
- asp.net response.ContentType 下载文件的四种方法
- [ASP.NET AJAX]Function对象及Type类的方法介绍
- ASP.NET基础教程-DataSet对象-属性方法和事件
- ASP.NET页面间传递对象的另类方法
- [转]asp.net response.ContentType 下载文件的四种方法
- asp.net的四种错误事件处理方法
- 在ASP.net 3.5中 用JSON序列化对象(两种方法)(转自LeonSky)
- ASP.NET Forum 中禁用含有子版块的版块后产生的"未将对象引用设置到对象的实例"异常的处理方法
- [学习笔记]ASP.NET需注意数据对象的释放
- 转载:在ASP.net 3.5中 用JSON序列化对象(两种方法)
- asp.net response.ContentType 下载文件的四种方法
- asp.net中怎样动态调用对象事件的处理方法?
- ASP中建立Excel对象,Excel进程总是无法释放(看了网上的很多帖子,两种方法,但是都不是很好)
- [ASP.NET AJAX]Function对象及Type类的方法介绍
- ASP.NET 2.0中几个对象的主要方法/事件流程
- asp.net连接池和SqlConnection对象的释放& "Timeout expired"异常