[英]Entity Framework Core Creation and Update fields
我的所有實體上都有四個自定義字段。 這四個字段是CreatedBy
、 CreatedDate
、 UpdatedBy
和UpdatedDate
。
有沒有辦法掛鈎到 Entity Framework 核心事件,以便在插入時它將使用當前的DateTime
填充CreatedDate
並使用當前用戶填充CreatedBy
? 當數據庫有更新時,它會用當前的DateTime
填充UpdatedDate
,用當前用戶填充UpdatedBy
?
基本上@Steve 的方法是可行的方法,但它的當前實現使得對您的項目進行單元測試變得困難。
通過一點點重構,您可以使其單元測試友好,並忠於 SOLID 原則和封裝。
這是 Steve 示例的重構版本
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.Name;
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();
}
}
現在很容易模擬單元測試的時間和用戶/主體,並且模型/域/業務層沒有 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
(直接或通過存儲庫)時,任何繼承AuditableEntity
實體都會根據需要設置其審計字段。
不幸的是,我在添加評論方面的聲譽很低。 出於這個原因,我添加了我的答案。 所以: 曾回答的一個補充。 當實體處於修改狀態時,您必須設置 IsModidied = false 或 CreatedBy 和 CreatedDate 屬性。 否則,這些字段將被字符串和日期時間類型的可選值重寫。
if (entry.State == EntityState.added)
{
CreatedBy = identityName ?? "unknown";
CreatedDate = now;
}
else
{
Entry<AuditableEntity>(entry).Property(p => p.CreatedBy).IsModified = false;
Entry<AuditableEntity>(entry).Property(p => p.CreatedDate).IsModified = false;
// or
// entity.Property("CreatedBy").IsModified = false;
// entity.Property("CreatedDate").IsModified = false;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.