【问题标题】:Entity Framework 6 Batch Updates and AuditLog using Entity Framework ExtendedEntity Framework 6 批量更新和 AuditLog 使用 Entity Framework Extended
【发布时间】:2014-07-31 17:23:44
【问题描述】:

我正在使用 Entity Framework 6,并且正在使用 EntityFramework Extended 来执行一些批量更新和批量删除。批量更新和批量删除工作正常,但是我还需要知道被更新/删除的实体(即当前和以前的值)。我认为使用 EntityFramework.Extended 提供的 AuditLogger 可以为我提供更新或删除的实体的详细信息,但似乎并非如此。例如,使用下面的代码,即

var auditor =dbContext.BeginAudit();
dbContext.Addresses.Update(ent => new Address { AddressId = 1190 });
dbContext.SaveChanges();
var changes = auditor.LastLog;

这是一个简单的批量更新,将所有 addressId 更新为 1190。 如果我检查changes.Entities,它会返回一个 0 的计数,即一个空列表。

我所期望的是changes.Entities 将包含所有具有旧值的“旧”实体,然后将 addressId 更改为1190。

是我弄错了还是这确实是正确的行为?使用实体框架扩展批量更新/删除时如何获取所有更新实体的审核日志

谢谢

【问题讨论】:

    标签: entity-framework entity-framework-5 entity-framework-6 entity-framework-extended


    【解决方案1】:

    您应该启用审核员

    var auditConfiguration = AuditConfiguration.Default;
    auditConfiguration.IncludeRelationships = true;
    auditConfiguration.LoadRelationships = true;
    auditConfiguration.DefaultAuditable = true;
    

    只需将其添加到您的应用初始化的位置,例如 Global.asax.cs

    已编辑

    据我所知,您无法使用 EF.Extended 获取旧值。 这是我的解决方案:

    在 Context 中覆盖 SaveChanges 方法

    public override int SaveChanges()
        {
            return SaveChanges(false);
        }
    
        public int SaveChanges(bool disableAudit)
        {
            var result = -1;
            try
            {
                if (!disableAudit)
                {
                    foreach (var entity in ChangeTracker.Entries().Where(x => x.State == EntityState.Added ||
                                                                              x.State == EntityState.Modified ||
                                                                              x.State == EntityState.Deleted))
                    {
                        ProccessAuditLog(entity);
                    }
                }
            }
            catch (Exception ex)
            {
                // handle the ex here
            }
            finally
            {
                //save changes
                result = base.SaveChanges();
            }
    
            return result;
        }
    

    并添加处理审计日志的方法:

    private void ProccessAuditLog(DbEntityEntry entry)
        {
            var entity = entry.Entity;
            var entityType = GetEntityType(entity.GetType());
    
            var oldValue = Activator.CreateInstance(entityType); ;
            if (entry.State == EntityState.Modified)
            {
                // entry.OriginalValues doesn't load navigation properties for changed entity so we should reload the object from db to get it
                // save current values
                var newValue = Activator.CreateInstance(entityType);
                Mapper.DynamicMap(entity, newValue, entity.GetType(), entityType);
    
                // reload old values for entity from the db
                entry.Reload();
                Mapper.DynamicMap(entry.Entity, oldValue, entity.GetType(), entityType);
    
                // revert reloading changes in entity
                entry.CurrentValues.SetValues(newValue);
                entry.OriginalValues.SetValues(oldValue);
                entry.State = EntityState.Modified;
                entity = newValue;
            }
    
            if (entry.State == EntityState.Deleted)
            {
                // reload old values for entity from the db
                entry.Reload();
                Mapper.DynamicMap(entry.Entity, oldValue, entity.GetType(), entityType);
    
                // revert reloading changes in entity
                entry.OriginalValues.SetValues(oldValue);
                entry.State = EntityState.Deleted;
                entity = null;
            }
    
            // here is you can proccess old entity in 'oldValue' and new entity in 'entity'
            // then save your log to db using SaveChanges(true) to prevent StackOverFlow exception
        }
    

    作为Mapper,你可以使用AutoMapper

    获取基础实体类型而不是代理类型的方法:

    private Type GetEntityType(Type entityType)
            {
                return entityType.BaseType != null && entityType.Namespace == "System.Data.Entity.DynamicProxies"
                        ? entityType.BaseType
                        : entityType;
            }
    

    希望对你有帮助

    【讨论】:

    • 嗨@Temaska,我已启用审核员,但我仍然得到相同的结果
    • 嗨@Temaska。我最终不得不使用与您提供的类似的东西,尽管在我的情况下,我必须在数据库上使用存储过程来进行审计。无论如何,您的解决方案也是正确的,因此我将其标记为正确答案。感谢您的帮助
    • @Temaska AuditConfiguration 的命名空间是什么?
    • @MohamedAhmed 它来自 EntityFramework.Extended: nuget.org/packages/EntityFramework.Extended
    猜你喜欢
    • 2011-09-06
    • 1970-01-01
    • 1970-01-01
    • 2017-09-17
    • 2016-09-13
    • 2017-08-05
    • 1970-01-01
    • 2014-08-29
    • 2014-11-11
    相关资源
    最近更新 更多