【问题标题】:EF Auditing CreationsEF 审计创作
【发布时间】:2016-05-15 06:20:37
【问题描述】:

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

当在我的 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"
        },
        ...
      ]
    }

【问题讨论】:

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


    【解决方案1】:

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

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

    我能想到的唯一解决方案是你覆盖原来的SaveChanges,并在覆盖的方法中这样做:

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

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

    【讨论】:

    • 嗨 JotaBe,这正是我在调用 AuditChanges() 然后调用基本 Savechanges 方法的重写 SaveChangesAsync() 方法中所做的,审核更改会在我的自定义 AuditEntry 表及其中生成这些额外的插入子 AuditDataItems。我的直觉是,我只能在基本的 Savechanges 执行后审核创建,但我猜如果我此时查询更改跟踪器,它会告诉我没有任何更改,所以我可以得到尚未创建的实体或一无所获......
    • 这里值得注意的是,出于日志记录/调试的原因,我也在拦截 SQL,但这是为了审计,所以我对 sql 不感兴趣,只对导致 for 之前和之后的行值感兴趣sql 执行……我说得通吗?
    • @Wardy 回答您的第一个问题:第一步:保留已添加实体的列表第三步:使用该列表。请注意,第一步在SaveChanges 之前,因此您会找到修改后的实体。在第三步中,您可以从存储在第一步中的列表中访问它们,您将找到实体的修改(现在未更改)版本。 EF 修改 dbcontext 中的实体,不要丢弃它们并创建新的实体。这就是它起作用的原因。
    • 这与 Jonathons 库的结合给了我一些想法,我将 +1 尝试一些东西......不过你是对的,流程和实体中基本上有 3 个步骤只能在添加发生后审核添加,我可能只需要重构我的调用代码以在保存之前调用我的非添加审核方法并在保存发生后添加
    【解决方案2】:

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

    这是一个例子:

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

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

    【讨论】:

    • +1 感谢您提供完整的解决方案,使用库是一种方法,但鉴于我在不到 50 行代码中有效地使用了相同的解决方案,我认为引入一个全新的库是矫枉过正.查看您的解决方案,我可以为我的案例额外增加 5 到 10 行代码来实现关键部分……有趣的方法!
    • 是的,我 100% 同意你的看法。这对你的情况来说太过分了。
    【解决方案3】:

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

    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 上下文的通用基类中,并运行迁移以将其应用到所有 db 和 bang ... 任何地方都会对所有标记为“IAmAuditable”的实体进行动态、自动审计(一个空标记界面)。

    我考虑过使用属性,但这需要反思,什么都不需要。

    【讨论】:

      猜你喜欢
      • 2012-08-19
      • 2021-08-06
      • 2017-06-29
      • 1970-01-01
      • 1970-01-01
      • 2016-09-15
      • 2013-06-28
      • 2018-09-19
      • 2021-05-20
      相关资源
      最近更新 更多