【问题标题】:IDisposable implementation when container is guaranteed to call Dispose method保证容器调用 Dispose 方法时的 IDisposable 实现
【发布时间】:2017-10-27 19:53:10
【问题描述】:

我已经使用 ASP.NET Core 构建了一个 API 服务。就像任何其他 API 一样,这个 API 必须从数据库中检索一些数据,应用一些业务逻辑,然后将数据发送回客户端。

首先,我有使用 Entity Framework.Core 搭建的 EmployeeDataContext 类。该类派生自Microsoft.EntityFrameworkCore.DbContext,如下所示。

public partial class EmployeeDataContext : DataContext
{
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        ......
    }

}

该数据上下文类在数据提供者类中使用如下。

public class EmployeeDataProvider : IEmployeeDataProvider, IDisposable
{
    private EmployeeDataContext dataContext;

    public EmployeeDataProvider(EmployeeDataContext context)
    {
        this.dataContext = context;
    }

    // Various CRUD methods


    // Dispose
    public void Dispose()
    {
        if ( this.dataContext != null )
        {
            this.dataContext.Dispose();
        }
    }
}

服务层持有对数据提供者的引用,如下所示。

public class EmployeeService : IEmployeeService
{
    private IEmployeeDataProvider dataProvider;

    public EmployeeService(IEmployeeDataProvider dataProvider)
    {
        DataProvider = dataProvider;
    }

    // Add/Delete/Update Employee related calls
}

所有的依赖都注入到Startup类中,如下所示。

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddScoped<IEmployeeDataProvider, EmployeeDataProvider>();
        services.AddScoped<IEmployeeService, EmployeeService>();
    }
}

根据微软doc

容器将为它创建的 IDisposable 类型调用 Dispose。

这意味着 EmployeeDataProvider.Dispose() 方法将在请求生命周期结束时被容器调用。

我的问题是关于如何为 EmployeeDataProvider 类实现 IDisposable。 link 提供了在各种情况下实现 IDisposable 的最佳实践,这些场景可能还需要您实现 Disposable(bool)。但是,对于这种情况,我不确定是否 所需的一切和​​我当前(简单)的 Dispose 实现已经足够好了,因为(因为这里不涉及通过终结器的调用)。对于这种情况,我的理解和 IDisposable 看起来是否正确?

【问题讨论】:

  • 如果您的班级是sealed,则不需要virtual Dispose(Boolean disposing) 方法。如果它是密封的,你只需要一个非虚拟的(如果你的类是子类,则覆盖)Dispose 方法,不带任何参数。

标签: dependency-injection asp.net-core .net-core idisposable asp.net-core-webapi


【解决方案1】:

在您的班级是sealed 的情况下,实现IDisposable 是微不足道的:

public sealed class Foo : IDisposable {

    private readonly FileStream stream;

    public Foo() {
        this.stream = new FileStream( ... );
    }

    public void Dispose() {

        this.stream.Dispose();
    }
}

您只需要protected virtual void Dispose(Boolean disposing) 方法,如果您的类将被子类化,则需要IDisposable 的推荐实现。

这在 FxCop 规则 CA1063“正确实施 IDisposable”的文档中有所描述:https://msdn.microsoft.com/en-us/library/ms244737.aspx

  • Dispose() 不是公开的、密封的或命名的 Dispose。
  • Dispose(bool) 未受保护、虚拟或未密封。
  • 在非密封类型中,Dispose() 必须调用 Dispose(true)。
  • 对于未密封的类型,Finalize 实现不会调用 Dispose(bool) 或案例类终结器中的任何一个或两者。

[...]

如何解决违规问题

[...] 确保 $className 被声明为 public 和密封。

另一个提示:如果您的字段仅在类型初始化程序或构造函数中分配 - 并且永远不应分配空值 - 那么您应该使用 readonly 修饰符(或使用只读自动属性 ​​-其中有一个readonly 支持字段),这样您就不需要在您的Dispose 方法中执行null-check。

注意Dispose()方法一般是幂等的:

https://msdn.microsoft.com/en-us/library/fs2xkftw.aspx

为帮助确保始终适当地清理资源,Dispose 方法应可多次调用而不会引发异常。

从历史上看,.NET 1.x 和 2.x 中的一些类如果被 Disposed 两次确实会抛出 ObjectDisposesException,但自从升级到 .NET 4.x 以来,我个人没有观察到非幂等行为- 尽管有可能一些写得不好的第三方库和组件可能会错误地实现它。

【讨论】:

  • 您能否提供任何有关自 .NET 4 以来调用 Dispose() 两次或更多次的安全性的链接?
  • 我仍然害怕Dispose() 的自定义/第三方实现,所以我通常使用Interlocked.Exchange(ref myField, null)?.Dispose()(这也是线程安全的。再次......比实际遇到问题更害怕)
  • 如果字段是通过构造函数依赖注入设置的,理论上它看起来可以为该字段分配空值。在那种情况下,我们不应该总是在 Dispose 方法中检查 null 吗?
  • @jimcrown 你应该在构造函数中检查null(然后抛出ArgumentNullException)——除非null 值是故意的并且对你的程序代码有用。
  • @jimcrown 我已经澄清了关于空值的答案。
猜你喜欢
  • 2011-06-29
  • 2011-05-25
  • 1970-01-01
  • 1970-01-01
  • 2011-01-20
  • 2011-07-17
  • 1970-01-01
  • 2011-03-08
  • 1970-01-01
相关资源
最近更新 更多