zoukankan      html  css  js  c++  java
  • ASP.NET Core 释放 IDisposable 对象的四种方法

    本文翻译自《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 = 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:UsersSockReposRegisterForDispose  
    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:UsersSockReposRegisterForDispose  
    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:UsersSockReposRegisterForDispose  
    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


    如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!

    欢迎各位转载,转载文章之后必须在文章页面明显位置给出作者和原文连接。

  • 相关阅读:
    比较全的JS checkbox全选、取消全选、删除功能代码
    JS下高效拼装字符串的几种方法比较与测试代码
    jQuery操作 input type=checkbox的实现代码
    禁用和选中所有页面上的复选框。
    jquery实现radio按纽全不选和checkbox全选的实例
    Javascript的四种继承方式
    javascript绑定时间 含(IE)
    input中如何输入逆写的中文句子
    清除浮动
    关于iOS9中的App Transport Security相关说明及适配(转)
  • 原文地址:https://www.cnblogs.com/tdfblog/p/four-ways-to-dispose-idisposables-in-asp-net-core.html
Copyright © 2011-2022 走看看