EF审计创作

EF Auditing Creations

好的,这里发生了很多事情,我不想用冗长的代码示例让你们感到厌烦,所以这里是摘录...

当在我的 EF 上下文中调用 SaveChangesAsync() 时,我让它调用此方法来审核每个条目...

async Task Audit(DbEntityEntry<IAmAuditable> entry)
{
    try
    {
        var newAuditEntry = new AuditEntry
        {
            EntityType = entry.Entity.GetType().Name,
            Event = entry.State.ToString(),
            SSOUserId = kernel.Get<User>().Id,
            EntityId = entry.GetId().ToString(),
            EventId = eventId
        };

如果有问题的条目是一个实体创建,这将导致在数据库上插入,那么我也会这样做...

var properties = entry.CurrentValues.PropertyNames.Select(p => entry.Property(p)).ToList();
var addedValues = new List<AuditDataItem>();

foreach (var p in properties)
{
    addedValues.Add(new AuditDataItem
    {
        PropertyName = p.Name,
        PreviousValue = null,
        NewValue = p.CurrentValue.ToString()
    });
}
newAuditEntry.Changes = addedValues;
break;

...这是它失败的地方...在那个时间点,对 SaveChanges 的基本调用尚未执行,因此所讨论的实体还没有主键值...最终结果是我记录了一个没有主要实体的创建。

有没有人对处理此问题的简洁方法有任何建议,以便我可以将新的主键值放入 AuditDataItem 中?

编辑:

这是我目前记录的示例 json,这是单个 AuditEntry 对象和部分 AuditDataItem 子行的一部分...

   {
      "Id": 4,
      "SSOUserId": 1,
      "EventId": "6d862aad-0898-4794-aea0-00af6f2994ff",
      "EntityType": "AC_Programme",
      "Event": "Added",
      "TimeOfEvent": "2016-02-04T12:04:31.5501508+01:00",
      "Changes": [
        {
          "Id": 34,
          "PropertyName": "Id",
          "PreviousValue": null,
          "NewValue": "0"
        },
        {
          "Id": 35,
          "PropertyName": "Name",
          "PreviousValue": null,
          "NewValue": "Test"
        },
        ...
      ]
    }

据我所知,在创建对象后,没有任何事件可以拦截对象创建。有以下活动:

第一次发生在保存更改之前(与您遇到的问题相同)。第二种情况发生在作为查询结果或 .Load 从数据库中读取实体时,因此它不适合您的情况。

我能想到的唯一解决办法就是重写原来的SaveChanges,在重写的方法中进行:

  • 查找处于已添加状态的所有实体,并保留对它们的引用,例如将它们添加到 List<Object>
  • 调用基础保存更改,以便在数据库中具体化更改,并更新 DbCOntext 中的实体
  • 访问列表中的实体,您将获得所有数据库生成的属性(如计算、身份、guid 等),以便您可以正确记录它们

Logging and Intercepting Database Operations (EF6 Onwards) 不符合您的需求

您需要将 ObjectStateEntry 保留在 AuditEntry 中,然后在 PostSaveChanges 事件中重新访问主键为 "Temporary" 的每个 AuditEntry。

这是一个例子:

显然,我建议您使用 EF+ Audit 而不是创建自己的库,但如果您仍想对其进行编码,该库是开源的,因此您可以找到很多信息来帮助您。

免责声明:我是项目的所有者EF+ (EntityFramework Plus)

好的,这就是我想出的...很想知道你们的想法...

public override async Task<int> SaveChangesAsync()
{
    try
    {
        await AuditChanges(new[] { EntityState.Modified, EntityState.Deleted });
        var result = await base.SaveChangesAsync();
        await AuditChanges(new[] { EntityState.Added });
        return result;
    }
    catch (DbEntityValidationException ex) { throw ConstructDetailsFor(ex); }
}

async Task AuditChanges(EntityState[] states)
{
    var auditableEntities = ChangeTracker.Entries<IAmAuditable>()
        .Where(e => states.Contains(e.State));

    foreach (var entry in auditableEntities)
        await Audit(entry);
}

async Task Audit(DbEntityEntry<IAmAuditable> entry)
{
    ...

这很简单:)

然后我的审计方法基本上进入一个 switch 语句,并根据从更改跟踪器传入的可审计实体条目来决定 运行 的逻辑。

我认为审计不会比这简单得多。

我将它放入通用基础 class 用于我所有的 EF 上下文和 运行 迁移以将其应用于所有数据库和 bang ... 到处都是动态的,所有的自动审计用 "IAmAuditable" 标记的实体(空标记界面)。

我考虑过使用一个属性,但这需要反射,什么不需要。