【问题标题】:get the id of a new record in SavingChanges event on the entityframework在实体框架上的 SavingChanges 事件中获取新记录的 id
【发布时间】:2010-07-21 14:31:49
【问题描述】:

我正在尝试使用实体上下文中的 SavingChanges 事件在实体框架 4.0 中实现一些通用日志记录。

我想记录有关任何创建/更新和删除记录的详细信息,包括正在更改的记录的 id(它始终是一个 int 身份字段)。在更新和删除过程中,我可以使用

获取记录的 id
    Dim AddedItems = Statemanager.GetObjectStateEntries(EntityState.Added)
    For Each entry As ObjectStateEntry In DirectCast(sender, ObjectContext).ObjectStateManager.GetObjectStateEntries(EntityState.Added)

        NewLogItem.RecordId = CInt(entry.EntityKey.EntityKeyValues(0).Value.ToString())
    Next

但我显然无法在插入过程中获取即将插入记录的 id,因为它尚未写入数据库,有没有简单的方法解决这个问题?

【问题讨论】:

    标签: asp.net vb.net entity-framework entity-framework-4


    【解决方案1】:

    您可以做的一个选择是覆盖 SaveChanges 方法并将 SaveChanges 调用到基础而不接受对 objectcontext 的更改。这样您就可以审核 ObjectStateManager 上的更改。

     override void SaveChanges(SaveOptions)
     {
    context.SaveChanges(SaveOptions.DetectChangesBeforeSave);
    //query the object statemanager
    
     }
    

    关键是使用正确的保存选项调用基础的保存更改。默认将接受对 ObjectStateManager 的所有更改,因此所有条目都将处于未更改状态,您将无法知道添加、删除或修改了什么。在遍历 statemanager 之后,您可以显式调用 AcceptChanges。 我也在书中介绍了这个概念。

    12-1 调用SaveChanges()时执行代码

    【讨论】:

    • 啊,有趣!我一直想知道AcceptAllChangesSaveChanges 相比的目的是什么。现在我看到了这个方法的一个有用的应用程序!
    【解决方案2】:

    这就是我最终的结果,并不理想,因为我结束了两个 saveChanges 调用,因此有两个事务,但看不到任何其他方式,如果我们在保存之前不记录已删除的项目,那么我们不能在 savechanges() 之后正确访问它们(实体将不再存在,这反映在已删除项目的状态条目中)

    公共函数 SaveAndLogChanges() As Integer

        Dim Statemanager As ObjectStateManager = Me.ObjectStateManager
    
        Dim AddedItems As IEnumerable(Of ObjectStateEntry) = Statemanager.GetObjectStateEntries(EntityState.Added)
        Dim UpdatedItems As IEnumerable(Of ObjectStateEntry) = Statemanager.GetObjectStateEntries(EntityState.Modified)
        Dim DeletedItems As IEnumerable(Of ObjectStateEntry) = Statemanager.GetObjectStateEntries(EntityState.Deleted)
    
        For Each entry As ObjectStateEntry In UpdatedItems
            ' dont do anything for any of the log file entries or we will end up in an infinate loop
            If (Not entry.EntitySet.Name = "LogItem" And Not entry.EntitySet.Name = "LogTransaction") Then
                createLogEntry(LogItemType.ItemType.Modify, entry)
            End If
        Next
    
        ' deleted entities
        For Each entry As ObjectStateEntry In DeletedItems
            ' dont do anything for any of the log file entries or we will end up in an infinate loop
            If (Not entry.EntitySet.Name = "LogItem" And Not entry.EntitySet.Name = "LogTransaction") Then
                createLogEntry(LogItemType.ItemType.Delete, entry)
            End If
        Next
    
        ' need to save changes here to ensure we have ids for the inserted records
        Me.SaveChanges()
    
        ' inserted enties
        For Each entry As ObjectStateEntry In AddedItems
            ' dont do anything for any of the log file entries or we will end up in an infinate loop
            If (Not entry.EntitySet.Name = "LogItem" And Not entry.EntitySet.Name = "LogTransaction") Then
                createLogEntry(LogItemType.ItemType.Insert, entry)
            End If
        Next
    
        Me.SaveChanges()
    
        Return iRet
    End Function
    

    【讨论】:

      【解决方案3】:

      我认为没有办法在SavingChanges 事件中获取新插入记录的 ID,因为该事件只是在完全触及数据库并创建 ID 之前调用。不幸的是,在保存数据后没有调用 SavedChanges 事件。

      一种可能的选择可能是利用 ObjectContext 的 SaveChanges 方法的重载之一是 virtual(或 VB 中的 overridable)这一事实,即:

      public virtual int SaveChanges(SaveOptions options)
      

      (注意:只有EF 4是虚拟的,早期版本没有!而且只有这个重载是虚拟的,SaveChanges的其他两个重载不是!)

      因此,您可以在模型的 ObjectContext 中覆盖此方法。在这个被覆盖的方法中,您调用基类的 SaveChanges,然后您的新实体应该具有从数据库中创建的 ID,然后您可以记录这些 ID。

      (这只是一个粗略的想法,我从未测试或使用过这个虚拟重载。我在 SavingChanges 中构建了一个类似的日志记录机制,就像你刚刚实现的那样,并且遇到了同样的问题。但这是在 EF 3.5 中这个虚拟的方法还不存在。最后我在调用 SaveChanges 后只在重要的地方记录了插入,但这当然不是“通用”的。也许新的虚拟方法现在是改进它的好机会。)

      编辑

      我的意思更准确一点(在 C# 语法中):

      public partial class MyEntitiesContext : ObjectContext
      {
          // ...
      
          public override int SaveChanges(SaveOptions options)
          {
              // Log something BEFORE entities are stored in DB
      
              int result = base.SaveChanges(options);
      
              // Log something AFTER entities are stored in DB, especially new entities
              // with auto-incrementing identity key which have been inserted in the DB
              // should have the final primary key value now
      
              return result;
          }
      
          // ...
      }
      

      重要的是致电base.SaveChanges(options)。 (当然,在没有 base. 的情况下只调用 SaveChanges(options) 会以 StackOverflow 结束。)

      【讨论】:

      • 嗨 Slauma,感谢您的帮助,无法让它工作,看起来 SaveChanges(SaveOptions options) 可能由无参数方法调用 SaveChanges() 调用(我需要调用从我覆盖的方法中),因为当我从覆盖的 SaveChanges(SaveOptions options) 方法中调用它时,它导致了 StackOverflowExcpetion。然而,它确实让我重新思考如何做到这一点,我最终在上下文中编写了自己的 SaveAndLogChanges 方法。
      • @Julian:您描述的 StackOverflowException 看起来很像您没有在覆盖的实现中调用基类的 SaveChanges 方法。我已经编辑了我的答案并添加了细节来强调这一点。
      • Hi Public Overrides Function SaveChanges(ByVal options As SaveOptions) As Integer Return MyBase.SaveChanges(options) End Function
      • 嗨 Slauma,除了调用 MyBase.SaveChanges() 之外,我所做的或多或少是我所做的,已更改为以下代码,它尝试在检索、编辑和保存时创建重复记录实体。 Public Overrides Function SaveChanges(ByVal options As SaveOptions) As Integer Return MyBase.SaveChanges(options) End Function 即使我要让它工作,我仍然需要对 savechanges() 进行 2 次调用以添加插入行的日志条目。感谢您的帮助,非常感谢
      猜你喜欢
      • 2013-06-02
      • 1970-01-01
      • 1970-01-01
      • 2021-02-08
      • 2018-12-12
      • 1970-01-01
      • 2023-04-01
      • 1970-01-01
      • 2021-09-21
      相关资源
      最近更新 更多