實體框架核心創建和更新字段

asp.net-core c# entity-framework-core

我的所有實體都有四個自定義字段。這四個字段是CreatedByCreatedDateUpdatedByUpdatedDate

有沒有辦法掛鉤到Entity Framework核心事件,以便在插入時它將使用當前用戶填充當前DateTimeCreatedByCreatedDate ?當有更新,這將填充數據庫UpdatedDate與當前DateTimeUpdatedBy與當前用戶?

一般承認的答案

基本上@ Steve的方法是可行的方法,但目前的實施方式使得很難對項目進行單元測試。

通過一些重構,您可以使其單元測試友好,並堅持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();
    }
}

現在,模擬時間和單元測試的用戶/主體很容易,模型/域/業務層沒有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實體都會根據需要設置審計字段。



Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow