在 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;
}
期待构建一个框架,(没有直接使用 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
AndStateChanged
events onChangeTracker
can be used to write logic that reacts to entities entering theDbContext
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;
}