【问题标题】:Multi-Hierarchy IDisposable Implementation for DbContext in C#C# 中 DbContext 的多层次 IDisposable 实现
【发布时间】:2015-04-11 04:20:23
【问题描述】:

基类:

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
}

子类:

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
}

逻辑类:

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 ....
    }
}

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

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

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

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

【问题讨论】:

  • 为什么不让context 受保护,而不是在基类中有一个不同的 字段?然后你不需要重写任何基类的 dispose 函数。事实上,您现在没有覆盖它们,而是隐藏它们。
  • 我不会在您的存储库中实现IDisposable,因为您要处理的唯一内容 (context) 不是由您的存储库创建的,而是提供给它的。并非所有的类都符合这一点,但是让创建一次性物品的东西成为处置它的物品是一种很好的做法。
  • 补充 Matthew 的正确观点,存储库应该持有对上下文的引用 - 而它应该为每个请求创建一个上下文,所以没有必要让你的存储库一次性。
  • @Matthew 我更新了我的问题。请重新阅读。
  • 首先尝试使用泛型,公共抽象类 Repository : IDisposable where T : DbContext

标签: c# entity-framework entity-framework-6


【解决方案1】:

如果您正在寻找通用一次性抽象类,请查看下面的类,但正如 @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
    }
}

编辑::::

你的逻辑类会是这样的

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

【讨论】:

  • 您发现了有关此实施的另一个问题。在你的代码中你有base(new AppContext()),然后你使用this.Context.Schedules。我没有看到您在 ctor 中实例化的包含 this.Context 的私有变量/字段。你能解释一下吗?
  • 在基础存储库中,我有一个 ctor,它采用 T where T:DBContext,并在驱动类 (ScheduleRepository) 中使用此上下文,我刚刚在存储库基础中创建了一个受保护的属性,以便我可以访问上下文'protected T Context { get { return _context; } }'
  • 我的意思是this.Context.Schedules在子类中的用法。我看到您在 ctor 中为基类创建了一个new AppContext(),但是您如何在子类中使用新实例化的上下文?我是否也需要在子类中创建一个新的上下文?
  • this.Context.Schedules ... 将使用您在 ctor new AppContext() 中实例化的那个,因为它将继承自基类 ...顺便说一下,Schedules 是一个名称我写的可能是您在 AppContext 类中有另一个属性名称,它引用了 DB 中的 Schedule 表
  • 啊。我忘记了“继承”。
猜你喜欢
  • 1970-01-01
  • 2017-01-21
  • 1970-01-01
  • 1970-01-01
  • 2013-09-27
  • 1970-01-01
  • 2013-10-29
  • 2018-12-30
  • 1970-01-01
相关资源
最近更新 更多