entity framework核心创建和更新字段

我的所有实体都有四个自定义字段。 这四个字段是CreatedByCreatedDateUpdatedByUpdatedDate

有没有办法挂钩到Entity Framework核心事件,以便在插入时它将使用当前用户填充当前DateTimeCreatedByCreatedDate ? 当数据库有更新时,它会使用当前用户填充当前DateTimeUpdatedBy吗?

基本上@ Steve的方法是可行的方法,但目前的实施方式使得很难对项目进行unit testing。

通过一些重构,您可以使其unit testing友好,并坚持SOLID原则和封装。

这是史蒂夫的例子​​的重构版本

 public abstract class AuditableEntity { public DateTime CreatedDate { get; set; } public string CreatedBy { get; set; } public DateTime UpdatedDate { get; set; } public string UpdatedBy { get; set; } } public class AuditableDbContext : DbContext { protected readonly IUserService userService; protected readonly DbContextOptions options; protected readonly ITimeService timeService; public BaseDbContext(DbContextOptions options, IUserService userService, ITimeService timeService) : base(options) { userService = userService ?? throw new ArgumentNullException(nameof(userService)); timeService = timeService ?? throw new ArgumentNullException(nameof(timeService)); } public override int SaveChanges() { // get entries that are being Added or Updated var modifiedEntries = ChangeTracker.Entries() .Where(x => (x.State == EntityState.Added || x.State == EntityState.Modified)); var identityName = userService.CurrentUser. var now = timeService.CurrentTime; foreach (var entry in modifiedEntries) { var entity = entry.Entity as AuditableEntity; if (entry.State == EntityState.Added) { entity.CreatedBy = identityName ?? "unknown"; entity.CreatedDate = now; } entity.UpdatedBy = identityName ?? "unknown"; entity.UpdatedDate = now; } return base.SaveChanges(); } } 

现在,模拟时间和unit testing的用户/主体很容易,模型/域/业务层没有EF Core依赖性,更好地封装了你的域逻辑方式。

当然,可以通过使用策略模式进一步重构这一点以使用更模块化的方法,但这超出了范围。 您还可以使用ASP.NET Core Boilerplate,它还提供可审计(和软删除)EF Core DbContext的实现( 此处和此处 )

我和我称之为“审计”字段的布局完全相同。

我解决这个问题的方法是创建一个名为AuditableEntity的基本抽象类来保存属性本身并公开一个名为PrepareSave的方法。 在PrepareSave我根据需要设置字段的值:

 public abstract class AuditableEntity { public DateTime CreatedDate { get; set; } public string CreatedBy { get; set; } public DateTime UpdatedDate { get; set; } public string UpdatedBy { get; set; } public virtual void PrepareSave(EntityState state) { var identityName = Thread.CurrentPrincipal.Identity.Name; var now = DateTime.UtcNow; if (state == EntityState.Added) { CreatedBy = identityName ?? "unknown"; CreatedDate = now; } UpdatedBy = identityName ?? "unknown"; UpdatedDate = now; } } 

我让PrepareSave虚拟的,所以如果我愿意,我可以在我的实体中覆盖它。 您可能需要根据实施情况更改获取标识的方式。

为了调用它,我在我的DbContext上覆盖了SaveChanges并在每个被添加或更新的实体(我从更改跟踪器获得)上调用了PrepareSave

 public override int SaveChanges() { // get entries that are being Added or Updated var modifiedEntries = ChangeTracker.Entries() .Where(x => x.State == EntityState.Added || x.State == EntityState.Modified); foreach (var entry in modifiedEntries) { // try and convert to an Auditable Entity var entity = entry.Entity as AuditableEntity; // call PrepareSave on the entity, telling it the state it is in entity?.PrepareSave(entry.State); } var result = base.SaveChanges(); return result; } 

现在,每当我在DbContext上调用SaveChanges时(直接或通过存储库),任何inheritanceAuditableEntity实体都会根据需要设置审计字段。