C# 中 DbContext 的多层次 IDisposable 实现

Multi-Hierarchy IDisposable Implementation for DbContext in C#

基础class:

public abstract class Repository : IDisposable
{
    private bool _disposed;
    private DbContext _context;
    public Repository(DbContext context)
    {
        _context = context;
    }

    public void SetSomething()
    {
        //...Access the database and set something for tracing
        _context.Database.SqlQuery(....);
    }

    public void UnSetSomething()
    {
        //...Access the database and cancel something for tracing
        _context.Database.SqlQuery(....);
    }

    #region Object Disposal
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    ~Repository()
    {
        Dispose(false);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
            return;

        if (disposing)
        {
            // free other managed objects that implement IDisposable only
            if (_context != null)
            {
                _context.Dispose();
                _context = null;
            }
        }

        _disposed = true;
    }
    #endregion
}

子 Class:

public class ScheduleRepository : Repository
{
    private AppContext _context;

    public ScheduleRepository(DbContext context)
        : base(context)
    {
        _context = (AppContext)context;
    }

    #region Object Disposal
    bool _disposed;
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    ~ScheduleRepository()
    {
        Dispose(false);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
            return;

        if (disposing)
        {
            // free other managed objects that implement IDisposable only
            if (_context != null)
            {
                _context.Dispose();
                _context = null;
            }
        }

        _disposed = true;

        base.Dispose(disposing);
    }
    #endregion
}

逻辑Class:

public class ScheduleFacade
{
    private ScheduleRepository _repository;
    public ScheduleFacade()
    {
        _repository = new ScheduleRepository(AppContext.Create());
    }

    public ScheduleSetting GetScheduleById(string scheduleId)
    {
        if (!string.IsNullOrEmpty(scheduleId))
        {
            _repository.SetSomething();

            ScheduleSetting settings = _repository.GetScheduleById(scheduleId);

            _repository.UnSetSomething();

            return LoadScheduleSettings(settings);
        }
        else
        {
            throw new ArgumentException("The scheduleId parameter cannot be empty.");
        }
    }

    private ScheduleSetting LoadScheduleSettings(ScheduleSetting settings)
    {
        //...code ....
    }
}

这是在抽象 class 实现上实现 IDisposable 的正确方法吗?这没有像它应该的那样遵循 DRY 原则,但我不清楚如何正确地做到这一点。

我想确保我正在适当地清理我的 DbContext

编辑:似乎需要更多信息来阐明我在做什么以及为什么我在构造函数中传入 DbContext(我在上面添加了更多代码,请重新阅读)。我需要抽象 class 中的 DbContext 来访问数据库并做一些工作。这难道不是我使用在多个子 class 之间共享的抽象 class 的方式,从而允许我遵守 DRY 原则并集中未来维护吗?

如果我不通过构造函数传递 DbContext,我将如何将它传递给抽象对象 class(我想到了方法注入,但这需要未来存储库的开发人员可能忘记将上下文传递给基础 class).

如果您正在寻找通用的一次性摘要 class 请查看下面的内容 class ,但是正如@Matthew 和@D Stanley 所说...存储库应该每次都初始化上下文.

public abstract class DisposableObject : IDisposable
{
    private bool _disposed = false;

    public virtual bool IsDisposed
    {
        get { return _disposed; }
    }

    protected void CheckDisposed()
    {
        if (IsDisposed)
        {
            throw new ObjectDisposedException(this.GetType().FullName);
        }
    }

    protected void CheckDisposed(string err)
    {
        if (IsDisposed)
        {
            throw new ObjectDisposedException(this.GetType().FullName, err);
        }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            OnDispose(disposing);
        }
        _disposed = true;
    }

    protected abstract void OnDispose(bool disposing); // this for the implementor to dispose their items

    ~DisposableObject()
    {
        Dispose(false);
    }

}

对于存储库模式的实现,它将是这样的:

public abstract class Repository<T> : IDisposable where T : DbContext
{
    private bool _disposed;
    private T _context;
    public Repository(T context)
    {
        _context = context;
    }

    protected T Context { get { return _context; } }

    public void SetSomething()
    {
        //...Access the database and set something for tracing
        // _context.Database.SqlQuery(....);
    }

    public void UnSetSomething()
    {
        //...Access the database and cancel something for tracing
        //_context.Database.SqlQuery(....);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (_context != null)
            {
                _context.Dispose();
                _context = null;
            }

            OnDispose(disposing);
        }
        _disposed = true;
    }

    // this for the implementor to dispose their items but not the context because it's already disposed from the base class
    protected abstract void OnDispose(bool disposing); 

    ~Repository()
    {
        Dispose(false);
    }
}

所以在你的 Schedule Repository 中这样做

public class ScheduleRepository : Repository<AppContext>
{

    public ScheduleRepository()
        :base(new AppContext())
    {

    }
    public Schedule GetById(int id) { 
        this.Context.Schedules. (....) // blah blah
    }

    protected override void OnDispose(bool disposing)
    {
        // if you are working with any thing that you must free it up
        // do it here, but not the context
    }
}

编辑::::

你的逻辑 class 看起来像那样

public class ScheduleFacade
{
    private ScheduleRepository _repository;
    public ScheduleFacade()
    {
        _repository = new ScheduleRepository();
    }

    public ScheduleSetting GetScheduleById(string scheduleId)
    {
        if (!string.IsNullOrEmpty(scheduleId))
        {
            _repository.SetSomething();

            ScheduleSetting settings = _repository.GetScheduleById(scheduleId);

            _repository.UnSetSomething();

            return LoadScheduleSettings(settings);
        }
        else
        {
            throw new ArgumentException("The scheduleId parameter cannot be empty.");
        }
    }

    private ScheduleSetting LoadScheduleSettings(ScheduleSetting settings)
    {
        //...code ....
    }
}

所以它不会依赖于 EF