EF审计创作

时间:2016-02-05 10:08:21

标签: c# entity-framework entity audit-logging

好的,这里发生了很多事情,我不想让你们知道一个很长的代码样本,所以这里有一个摘录......

在我的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
        };

如果有问题的条目是实体创建,将导致db i上的插入,那么也会这样做...

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"
        },
        ...
      ]
    }

3 个答案:

答案 0 :(得分:1)

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

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

我能想到的唯一解决方案是覆盖原始的SaveChanges,并在重写的方法中执行此操作:

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

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

答案 1 :(得分:1)

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

以下是一个例子:

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

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

答案 2 :(得分:0)

好的,这就是我想出来的......好奇地想知道你们的想法......

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语句,并根据来自更改跟踪器的传入的可审计实体条目决定运行什么逻辑。

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

我把它放到我所有EF上下文的通用基类中,然后运行迁移将其应用到所有数据库和bang ...各处获得动态,自动审核所有标有“IAmAuditable”的实体(空标记界面。

我考虑过使用属性,但这需要反思而不是。