实体框架核心创建和更新字段

时间:2017-04-10 03:38:53

标签: c# asp.net-core entity-framework-core

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

是否有办法挂钩实体框架核心事件,以便在插入时,它将使用当前用户填充当前CreatedDateDateTime的{​​{1}}?当数据库有更新时,它会使用当前用户填充CreatedBy当前UpdatedDateDateTime吗?

3 个答案:

答案 0 :(得分:9)

基本上,@ 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(herehere)的实现

答案 1 :(得分:2)

我的布局与我所说的完全相同"审核"字段。

我解决这个问题的方法是创建一个名为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设为虚拟,因此如果需要,我可以在我的实体中覆盖它。您可能需要根据实施情况更改身份获取方式。

为了打电话给我,我在我的SaveChanges上覆盖DbContext,并在每个添加或更新的实体(我从更改跟踪器获得)上调用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的实体都会根据需要设置审核字段。

答案 2 :(得分:0)

很遗憾,我的声誉因添加评论而很低。因此,我添加了我的答案。所以: 曾答一。当实体处于修改状态时,必须将IsModidied = false设置为CreatedBy和CreatedDate属性。否则,将使用字符串和DateTime类型的可选值重写该字段。

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;
}