Я реализовал аналогичное решение о том, как мы можем изменить дату создания и обновления при сохранении данных через EF Core, как это предлагается здесь Автоматическое заполнение Created и LastModified в 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 вызывается в приведенном выше сценарии?





Я предлагаю вам переопределить метод SaveChanges в вашем dbContext. Вы можете обратиться к приведенному ниже коду, который я обычно использую.
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);
}
Я подумал, что вам может быть интересно узнать, почему вы получаете события, которые вы видели в своем вопросе.
Когда вы выполняете строку 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;
}
Метод Entry EF Core делает это, потому что вы можете запросить State объекта, и поэтому он должен вызвать DetectChanges, чтобы убедиться, что он обновлен.
Теперь оказывается, что если вы сделаете следующее
student.Name = "New student name";
_dbContext.SaveChanges();
Затем (в предварительной версии EF Core 5, но я думаю, что в EF Core 3.1 то же самое) вы получаете два события.
DetectChanges.SaveChanges, когда он устанавливает состояние, указывающее, что база данных соответствует классу сущности.Если вы сделаете следующее
student.Name = "New student name";
var entry = _dbContext.Entry(student);
_dbContext.SaveChanges();
Тогда вы получите те же события. DetectChanges будет вызываться дважды (один раз Entry и один раз SaveChanges), но при втором вызове DetectChanges состояние не изменится.
Вы можете увидеть это в мои модульные тесты в репозитории, который я пишу в поддержку моей книги Ядро Entity Framework в действии. Я пишу раздел об этих событиях и нашел ваш вопрос, и я хотел бы ответить на него.
Я надеюсь, что это поможет вам понять, что происходит, но я должен сказать, что другие ответы, предлагающие переопределение SaveChanges, являются лучшим решением, чем использование этих событий.
Спасибо за объяснение, это действительно полезно
переопределить
public virtual int SaveChanges(bool acceptAllChangesOnSuccess)— там вы можете получить доступ кChangeTracker, найти все объекты с состояниемModifiedи обновить их свойствоLastModifiedпрямо перед записью в БД.