zoukankan      html  css  js  c++  java
  • 在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:UsersSockReposRegisterForDispose
    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在构造函数中!

    RegisterForDisposenew在代码中建立服务时很有用但是,鉴于只有使用非托管资源的类才需要使用Dispose,因此您可能会发现,通常情况下,您的IDisposable类封装在使用DI容器注册的服务中。

    正如Mark Rendle所指出的那样,其Controller本身也将在请求结束时进行处理,因此您可以使用该机制来处理您创建的任何对象。

    自动处理服务-利用内置的DI容器

    ASP.NET Core带有一个简单的内置DI容器,您可以将其注册为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 作为暂时的
    • 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: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 从来没有处置过,因为它是提供给特定实例的!

    请注意,仅处理由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: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语句,或让DI容器为您处理对象。对于无法实现的情况,ASP.NET Core提供了两种可以连接的机制:RegisterForDisposeIApplicationLifetime

  • 相关阅读:
    你的想像力智商有多高?
    Visual FoxPro 9.0 发布
    Google的社会网络
    女人永远是对的
    如何保存ICQ聊天历史
    7 30 个人赛
    Linux下利用文件描述符恢复的成功失败实验
    蓝鲸社区版部署
    Oracle 10.2.0.5升级至11.2.0.4
    手动创建Oracle实例
  • 原文地址:https://www.cnblogs.com/cxxtreasure/p/14357905.html
Copyright © 2011-2022 走看看