entity framework核心创建和更新字段
我的所有实体都有四个自定义字段。 这四个字段是CreatedBy
, CreatedDate
, UpdatedBy
和UpdatedDate
。
有没有办法挂钩到Entity Framework核心事件,以便在插入时它将使用当前用户填充当前DateTime
和CreatedBy
的CreatedDate
? 当数据库有更新时,它会使用当前用户填充当前DateTime
和UpdatedBy
吗?
基本上@ 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
实体都会根据需要设置审计字段。
- AuthorizationOptions上的dependency injection
- 如何在请求结束时释放资源并在ASP.NET 5 / Core中处理注入的服务?
- 如何在环境变量中正确存储连接字符串,以便通过生产ASP.Net Core MVC应用程序进行检索
- ASP.NET MVC 6:在单独的程序集中查看组件
- 将ArrayPool对象提供给JsonOutputFormatter构造函数
- ASP.NET Core错误:实施忘记密码时出现无效令牌错误
- 如何在ASP.NET Boilerplate中设置社交登录?
- 如何在asp.net中的动作filter中添加参数?
- ASP .NET核心输入标记助手不使用Razor代码