调用.Entry()时会触发DbContext的ChangeTracker.StateChanged

时间:2019-03-20 02:41:37

标签: c# asp.net-core entity-framework-core ef-core-2.1

我实施了类似的解决方案,关于如何通过EF Core保存数据时修改创建日期和更新日期的建议,Populate Created and LastModified automagically in EF Core

void OnEntityStateChanged(object sender, EntityStateChangedEventArgs e)
{
    if (e.NewState == EntityState.Modified && e.Entry.Entity is IHasCreationLastModified entity)
        entity.LastModified = DateTime.Now;
}

起初,我认为只有在调用SaveChanges()时才会触发此操作。但显然,它也被称为Entry()

// Get entity
var student = _dbContext.Students.Find(studentId);

// Modify student object
student.Name = "New student name";

// Called Entry(), trigger ChangeTracker.StateChanged
var entry = _dbContext.Entry(student);

// Doesn't trigger ChangeTracker.StateChanged
_dbContext.SaveChanges();

我发现调用ChangeTracker.StateChanged时触发了_dbContext.Entry(student)。这样,在调用_dbContext.SaveChanges()时不会再次触发它。并且还通过了if (e.NewState == EntityState.Modified && e.Entry.Entity is IHasCreationLastModified entity)以上的条件。

我的假设为什么在调用SaveChanges()时不会再次触发它,因为在调用Entity()之后没有新的实体更新。

这将导致在调用LastModified时而不是在调用.Entry(student)时分配.SaveChanges()属性。

在上述情况下调用LastModified时,是否只能将SaveChanges属性更新一次?

2 个答案:

答案 0 :(得分:1)

我建议您可以在dbContext中覆盖SaveChanges方法。您可以参考下面我经常使用的代码。

public class ForumContext : DbContext
{
    public ForumContext(DbContextOptions<ForumContext> options) : base(options)
    {

    }
    //other settings
    public override int SaveChanges(bool acceptAllChangesOnSuccess)
    {
        foreach (var entry in ChangeTracker.Entries())
        {
            switch (entry.State)
            {
                case EntityState.Added:
                    ((BaseEntity)entry.Entity).AddedDate = DateTime.Now;
                    ((BaseEntity)entry.Entity).LastModified = DateTime.Now;
                    break;

                case EntityState.Modified:
                    ((BaseEntity)entry.Entity).LastModified = DateTime.Now;
                    break;

                case EntityState.Deleted:
                    entry.State = EntityState.Modified;
                    entry.CurrentValues["IsDeleted"] = true;
                    break;
            }
        }
        return base.SaveChanges(acceptAllChangesOnSuccess);
    }

答案 1 :(得分:1)

我想您可能想知道为什么会收到在问题中看到的事件。

执行行student.Name = "New student name";时,默认情况下,由于EF Core尚未调用ChangeTracker.DetectChanges方法,所以它什么也不发生。

但是对var entry = _dbContext.Entry(student);的调用会运行ChangeTracker.DetectChanges的一个版本-请参阅下面的代码,摘自EF Core代码。

public virtual EntityEntry<TEntity> Entry<TEntity>([NotNull] TEntity entity) where TEntity : class
{
    Check.NotNull<TEntity>(entity, nameof (entity));
    this.CheckDisposed();
    EntityEntry<TEntity> entityEntry = this.EntryWithoutDetectChanges<TEntity>(entity);
    //My comment - this runs a version of the DetectChanges method. 
    this.TryDetectChanges((EntityEntry) entityEntry);
    return entityEntry;
}

EF Core的Entry方法之所以这样做,是因为您可能要求实体的State,因此它必须调用DetectChanges以确保其最新。

现在,事实证明,如果您执行以下操作

student.Name = "New student name";
_dbContext.SaveChanges();

然后(在EF Core 5预览版中,但我认为在EF Core 3.1中是相同的),您将获得两个事件。

  1. OldState.EntityState ==不变,newState.EntityState ==已修改-通过调用DetectChanges触发。
  2. OldState.EntityState ==修改后,newState.EntityState ==不变-由SaveChanges触发,当它设置状态说数据库与实体类匹配时。

如果您执行以下操作

student.Name = "New student name";
var entry = _dbContext.Entry(student);
_dbContext.SaveChanges();

然后,您将获得相同的事件。 DetectChanges会被调用两次(一次被Entry调用,一次被SaveChanges调用),但是第二次调用DetectChanges

的状态没有变化

您可以在我为支持我的书my unit tests而编写的回购中的Entity Framework Core in Action中看到此内容。我正在撰写有关这些事件的部分,找到了您的问题,尽管我会回答。

我希望它可以帮助您了解正在发生的事情,但是我应该说,其他的建议覆盖SaveChanges的解决方案比使用这些事件更好。