获取entityframework上SavingChanges事件中新记录的id

时间:2010-07-21 14:31:49

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

尝试使用实体上下文中的SavingChanges事件在entityframework 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,因为它尚未写入数据库,是否有一个简单的方法呢?

3 个答案:

答案 0 :(得分:3)

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

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

 }

关键是使用正确的saveoptions将savechanges调用到base。默认值将接受对ObjectStateManager的所有更改,因此所有条目将处于未处理状态,您将无法知道添加,删除或修改的内容。在循环遍历statemanager之后,您可以显式调用AcceptChanges。 我也在书中介绍了这个概念。

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

答案 1 :(得分:0)

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

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

public virtual int SaveChanges(SaveOptions options)

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

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

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

修改

我的意思更加精确(在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)。 (当然,只调用SaveChanges(options)base.不会以StackOverflow结束。)

答案 2 :(得分:0)

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

Public Function 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