【问题标题】:entity framework 5 change log how to implement?实体框架5变更日志如何实现?
【发布时间】:2014-01-06 14:25:59
【问题描述】:

我正在使用 MVC4 和实体框架 5 创建一个应用程序。我该如何实现它? 我环顾四周,发现我需要覆盖 SaveChanges 。

有人有这方面的示例代码吗?我使用的是代码优先方法。

例如,我保存数据的方式如下,

 public class AuditZoneRepository : IAuditZoneRepository
    {
        private AISDbContext context = new AISDbContext();



        public int Save(AuditZone model, ModelStateDictionary modelState)
        {
            if (model.Id == 0)
            {
                context.AuditZones.Add(model);
            }
            else
            {
                var recordToUpdate = context.AuditZones.FirstOrDefault(x => x.Id == model.Id);
                if (recordToUpdate != null)
                {
                    recordToUpdate.Description = model.Description;
                    recordToUpdate.Valid = model.Valid;
                    recordToUpdate.ModifiedDate = DateTime.Now;
                }
            }

            try
            {
                context.SaveChanges();
                return 1;
            }
            catch (Exception ex)
            {
                modelState.AddModelError("", "Database error has occured.  Please try again later");
                return -1;
            }
        }
    }

【问题讨论】:

标签: asp.net-mvc-4 entity-framework-5


【解决方案1】:

无需覆盖 SaveChanges。

你可以

触发器Context.ChangeTracker.DetectChanges(); // 可能是必要的,具体取决于您的代理方法

然后在保存之前分析上下文。 然后,您可以...将更改日志添加到当前工作单元。 所以日志被保存在一个 COMMIT 事务中。 或按您认为合适的方式处理它。 但同时保存您的更改日志。确保它是 ONE Transaction。

分析上下文样本: 我有一个简单的工具,可以转储上下文内容以调试输出,因此在调试器中我可以使用即时窗口来检查内容。例如 您可以将其用作准备更改日志的起点。 在调试器即时窗口中尝试。我的 Context 类上有 FULL 转储。

示例立即窗口调用。 UoW.Context.FullDump();

public void FullDump() {

        Debug.WriteLine("=====Begin of Context Dump=======");
        var dbsetList = this.ChangeTracker.Entries();
        foreach (var dbEntityEntry in dbsetList)
        {

            Debug.WriteLine(dbEntityEntry.Entity.GetType().Name + " => " + dbEntityEntry.State);
            switch (dbEntityEntry.State)
            {
                case EntityState.Detached:
                case EntityState.Unchanged:
                case EntityState.Added:
                case EntityState.Modified:
                    WriteCurrentValues(dbEntityEntry);
                    break;
                case EntityState.Deleted:
                    WriteOriginalValues(dbEntityEntry);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            Debug.WriteLine("==========End of Entity======");
        }
        Debug.WriteLine("==========End of Context======");
    }

    private static void WriteCurrentValues(DbEntityEntry dbEntityEntry)
    {
        foreach (var cv in dbEntityEntry.CurrentValues.PropertyNames)
        {
            Debug.WriteLine(cv + "=" + dbEntityEntry.CurrentValues[cv]);
        }
    }
    private static void WriteOriginalValues(DbEntityEntry dbEntityEntry)
    {
        foreach (var cv in dbEntityEntry.OriginalValues.PropertyNames)
        {
            Debug.WriteLine(cv + "=" + dbEntityEntry.OriginalValues[cv]);
        }
    }
}

编辑:获取更改

我使用此例程来获取更改...

public class ObjectPair {
    public string Key { get; set; }
    public object Original { get; set; }
    public object Current { get; set; }
 }

public virtual IList<ObjectPair> GetChanges(object poco) {
        var changes = new List<ObjectPair>();
        var thePoco = (TPoco) poco;


        foreach (var propName in Entry(thePoco).CurrentValues.PropertyNames) {
            var curr = Entry(thePoco).CurrentValues[propName];
            var orig = Entry(thePoco).OriginalValues[propName];
            if (curr != null && orig != null) {
                if (curr.Equals(orig)) {
                    continue;
                }
            }
            if (curr == null && orig == null) {
                continue;
            }
            var aChangePair = new ObjectPair {Key = propName, Current = curr, Original = orig};
            changes.Add(aChangePair);
        }
        return changes;
    }

edit 2 如果必须使用内部对象跟踪。

    var context =  ???// YOUR DBCONTEXT class
     // get objectcontext from dbcontext... 
     var   objectContext = ((IObjectContextAdapter) context).ObjectContext;
        // for each tracked entry
        foreach (var dbEntityEntry in context.ChangeTracker.Entries()) {
          //get the state entry from the statemanager per changed object
          var stateEntry =  objectContext.ObjectStateManager.GetObjectStateEntry(dbEntityEntry.Entity);
            var modProps = stateEntry.GetModifiedProperties();
            Debug.WriteLine(modProps.ToString());
        }

我反编译了 EF6 。得到修改确实是使用私有位数组来跟踪具有 已更改。

 // EF decompiled source..... _modifiedFields is a bitarray
 public override IEnumerable<string> GetModifiedProperties()
{
  this.ValidateState();
  if (EntityState.Modified == this.State && this._modifiedFields != null)
  {
    for (int i = 0; i < this._modifiedFields.Length; ++i)
    {
      if (this._modifiedFields[i])
        yield return this.GetCLayerName(i, this._cacheTypeMetadata);
    }
  }
}

【讨论】:

  • Phil - 这是一个很棒的示例。谢谢你。我已经更新了上面的代码,我没有使用工作单元。所以你是说在我保存之前我应该​​更改跟踪器来比较两组值吗?另外,如果我有不同的存储库,我是否必须复制代码?
  • 拥有许多存储库是可以的。该代码用于上下文,因此可以将其分解。即只是在上下文中传递。考虑工作单元方法,您控制保存的位置是控制更改日志的位置。 UOW 模式存在的原因是为了避免在存储库中使用 500 个保存方法;-)
  • phil - 有没有一种简单的方法可以只识别修改后的值?在记录中,我可能只更改 1 个字段。那么当前值函数是只显示已修改的项目还是显示该记录的所有字段?
  • Phil - 这可能听起来很傻,但是当 EF 将要更新的数据传递到基础表时,它肯定只会向已更改的值发送更新语句,是吗?即在一行10个字段中,如果只更新1个字段,发送到表的sql应该只更新1个字段?如果是这种情况,那么 ChangeTracker 只会给我更改的数据吗?因为我只想记录这些...
  • 正确的 EF 执行更新集。让我看看我是否可以快速访问该信息
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-24
  • 1970-01-01
  • 2020-02-06
相关资源
最近更新 更多