在 EF Core 中自动填充 Created 和 LastModified

Populate Created and LastModified automagically in EF Core

期待构建一个框架,(没有直接使用 DbSets 的存储库模式)自动填充创建和上次修改,而不是通过代码库吐出这些代码。

你能指出正确的方向如何实现它吗?

In past I tried populating these in constructors, however that seems like a nasty code and every time we pull up somting from database EF change tracking will mark the entity as modified.

.ctor()
    {
        Created = DateTime.Now;
        LastModified = DateTime.Now;
    }

public interface IHasCreationLastModified
    {
        DateTime Created { get; set; }
        DateTime? LastModified { get; set; }
    }

public class Account : IEntity, IHasCreationLastModified
{
    public long Id { get; set; }

    public DateTime Created { get; set; }
    public DateTime? LastModified { get; set; }
    public virtual IdentityUser IdentityUser { get; set; }
}

一种可能的解决方案(我们目前在我工作的地方使用的解决方案)是重写 DbContext 中的 SaveChanges() 方法并添加一些代码来循环遍历设置值的已更改实体

public override int SaveChanges()
{
    var changedEntriesCopy = ChangeTracker.Entries()
                .Where(e => e.State == EntityState.Added ||
                e.State == EntityState.Modified ||
                e.State == EntityState.Deleted)
                .ToList();
    var saveTime = DateTime.Now;

        foreach (var entityEntry in changedEntriesCopy)
        {
            if (entityEntry.Metadata.FindProperty("Created") != null && entityEntry.Property("Created").CurrentValue == null)
            {
                entityEntry.Property("Created").CurrentValue = saveTime;
            }

            if (entityEntry.Metadata.FindProperty("Updated") != null)
            {
                entityEntry.Property("Updated").CurrentValue = saveTime;
            }
        }
    return base.SaveChanges();
}

我们的情况基本上与此类似 - 在我们的情况下 Created 可以为 null,因此当创建新实体时,在创建之前的代码中该值为 null(便于检查 Created 是否曾经被填充过).

可能还有其他解决方案,但这对我们来说是最容易设置的,并且没有任何明显的性能影响

从 v2.1 开始,EF Core 提供 State change events:

New Tracked And StateChanged events on ChangeTracker can be used to write logic that reacts to entities entering the DbContext or changing their state.

您可以从 DbContext 构造函数

中订阅这些事件
ChangeTracker.Tracked += OnEntityTracked;
ChangeTracker.StateChanged += OnEntityStateChanged;

然后做这样的事情:

void OnEntityTracked(object sender, EntityTrackedEventArgs e)
{
    if (!e.FromQuery && e.Entry.State == EntityState.Added && e.Entry.Entity is IHasCreationLastModified entity)
        entity.Created = DateTime.Now;
}

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