在EntityFrameworks DbContext中跟踪更改的通知

本文关键字:通知 跟踪 EntityFrameworks DbContext | 更新日期: 2023-09-27 18:13:37

我正在使用实体框架构建一个数据输入应用程序,用户可以填写表单,然后保存或取消。但是,我希望保存按钮只有在有实际数据可以保存到数据库时才启用。

我知道DbContext.ChangeTracker。然而,我无法找到一种可能性,无论何时有变化,都可以从上下文获得通知。

当然,我可以手动跟踪,但这是乏味和容易出错的。

该应用程序是一个WinForms应用程序

问题:我怎么能从DbContext得到一个通知,当它"脏/有变化"?

更新2

也许这可以澄清我的问题:

这是我不想要的:

using(var ctx = new DbContext()) {
  var foo = new FooEntity();
  ctx.Add(foo);
  RaiseContextIsDirty();  //<-- don't want to do this, this should be automatic
  //.....
  ctx.SaveChanges();
  RaiseContextIsClean();  //<-- don't want to do this, this should be automatic
}

我要找的是这样的东西:

using(var ctx = new DbContext()) {
  ctx.ChangeTracker.OnDirtyChanged += ContextDirtyChanged;
  var foo = new FooEntity();
  ctx.Add(foo);   //<- fires OnDirtyChanged
  //.....
  ctx.SaveChanges();   //<- fires OnDirtyChanged
}

在EntityFrameworks DbContext中跟踪更改的通知

您可以对DbContext.HasUnsavedChanges属性使用以下代码:

public partial class MyDBContext 
{
    public bool HasUnsavedChanges
    {
        get
        {
            try
            {
                return this.ChangeTracker.Entries().Any(e => e.State == EntityState.Added
                                                          || e.State == EntityState.Modified
                                                          || e.State == EntityState.Deleted);
            }
            catch (System.InvalidOperationException)
            {
                return false;
            }
        }
    }

在WPF中,将此设置为WPF命令SaveCanExecute条件就足够了。

Windows窗体的问题是,你需要找到一个事件,经常触发和禁用或启用按钮,但不是太频繁。我不认为在EntityFramework中有任何内置的属性观察者会为你提供这样的事件。

你可以检查dbContext.HasUnsavedChanges在每个鼠标上或键上事件的形式或类似的东西,这是类似于如何WPF内部工作,见- http://northhorizon.net/2012/better-commands-in-wpf/。

所以你可以重写SaveChanges:

public class MyDbContext : DbContext
{
   public delegate void ChangeEventHandler(object sender, ChangeEventArgs e);
   public event ChangeEventHandler ChangeEvent;
   public override int SaveChanges()
   {
       var result = base.SaveChanges();
       var entitiesAdded = this.ChangeTracker.Entries.Where(x => x.State == EntityState.Added);
       var entitiesRemoved = this.ChangeTracker.Entries.Where(x => x.State == EntityState.Deleted);
       var entitiesModified = this.ChangeTracker.Entries.Where(x => x.State == EntityState.Modified);
       ChangeEvent(this, new ChangeEventArgs(entitiesAdded, entitiesRemoved, entitiesModified));
       return result;
   }
}

通过创建实现EventArgs的类来创建ChangeEventArgs

那么你应该能够以正常的方式绑定到事件:

ctx.ChangeEvent += (s, e) => Console.WriteLine("Changes happened");

编辑

我刚刚从MSDN中提取了大部分内容,我没有时间进行适当的测试。但我不认为这个解决方案错得太离谱。

进一步想法

DbSet<T>回到IDbSet<T>上,所以你没有理由不能实现自己的IDbSet<T>,当你实现自己的Add功能时,将事件连接进来。这将遵循与上面相同的模式。

然后在你的MyDbContext中听取所有这些事件并将它们合并为一个事件或在它们出现时处理它们