Entity framework 实体框架:如何在断开连接的场景中更新具有独立关联的实体?

Entity framework 实体框架:如何在断开连接的场景中更新具有独立关联的实体?,entity-framework,Entity Framework,我正在尝试更新一个具有独立关联的对象,该关联已在UI中更改。加载和保存在不同的DBContext中进行。我试过以下方法,但现在我被卡住了。我时不时地遇到DbUpdateException EFTestConsole.UserList.Update: System.Data.Entity.Infrastructure.DbUpdateException: An error occurred while saving entities that do not expose foreign key p

我正在尝试更新一个具有独立关联的对象,该关联已在UI中更改。加载和保存在不同的DBContext中进行。我试过以下方法,但现在我被卡住了。我时不时地遇到DbUpdateException

EFTestConsole.UserList.Update: System.Data.Entity.Infrastructure.DbUpdateException: An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details. ---> System.Data.Entity.Core.OptimisticConcurrencyException: Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.
就我所见,这源于现有对象之间的现有关系,这些对象是由实体框架添加的,不包含在导航属性中,并且在绘制状态时我没有将其设置为“未更改”。但如何识别它们呢

这就是我到目前为止所做的:

  • 将修改后的对象添加到DBContext,并将其状态设置为modified
  • 通过将其状态设置为“已删除”,删除与已从导航属性中删除的对象(存在于数据库中)的关系
  • 绘制导航属性中当前包含的所有对象的状态:将现有对象(ID>0)和关系的状态设置为“未更改”,并将新对象(ID=0)及其关系的状态设置为“已添加”
  • 绘制添加到导航属性的对象的状态:将对象的状态设置为“未更改”(如果已存在:ID>0)或“已添加”(如果新:ID=0),并将关系的状态设置为“已添加”
  • 从DBContext中删除所有对象和关系,这些对象和关系由实体框架添加到上下文中,但不包含在(旧的或当前的)导航属性中
  • 代码如下:

    Public Function Update(ByVal userToProcess As User) As Boolean
        Dim Success As Boolean = False
                ' Compute, which objects in the navigation properties were added resp. removed.
    
                ' Register userToProcess in TempDBContext.
                TempDBContext.UserDBSet.Add(userToProcess)
                TempDBContext.Entry(userToProcess).State = EntityState.Modified
    
                ' Fixing relationships to objects in navigation properties in 3 steps:
                ' 1. Deleting relationships to objects, which remain in database.
                For Each adg As ADGroup In UsersADGroupsToDelete
                    If TempDBContext.Entry(adg).State = EntityState.Detached Then
                        adg = TempDBContext.ADGroupDBSet.Find(adg.ID)
                        TempDBContext.Entry(adg).State = EntityState.Unchanged
                    End If
                    TempDBContext.Core.ObjectStateManager _
                        .ChangeRelationshipState( _
                            userToProcess, _
                            adg, _
                            "UsersADGroupsList", _
                            EntityState.Deleted)
                Next
    
                For Each ug As Group In UsersGroupsToDelete
                    If TempDBContext.Entry(ug).State = EntityState.Detached Then
                        ug = TempDBContext.GroupDBSet.Find(ug.ID)
                        TempDBContext.Entry(ug).State = EntityState.Unchanged
                    End If
                    TempDBContext.Core.ObjectStateManager _
                        .ChangeRelationshipState( _
                            userToProcess, _
                            ug, _
                            "UsersGroupsList", _
                            EntityState.Deleted)
                Next
    
                ' 2.1. Setting objects and relationships to Unchanged for all existing objects in navigation properties.
                ' 2.2. Settings objects and relationships to Added for all new objects (ID = 0) in naviagtion properties.
                For Each adg As ADGroup In userToProcess.UsersADGroupsList
                    If adg.ID > 0 Then
                        TempDBContext.Entry(adg).State = EntityState.Unchanged
                        TempDBContext.Core.ObjectStateManager _
                            .ChangeRelationshipState( _
                                userToProcess, _
                                adg, _
                                "UsersADGroupsList", _
                                EntityState.Unchanged)
                    Else
                        TempDBContext.Entry(adg).State = EntityState.Added
                        TempDBContext.Core.ObjectStateManager _
                            .ChangeRelationshipState( _
                                userToProcess, _
                                adg, _
                                "UsersADGroupsList", _
                                EntityState.Added)
                    End If
                Next
    
                For Each ug As Group In userToProcess.UsersGroupsList
                    If ug.ID > 0 Then
                        TempDBContext.Entry(ug).State = EntityState.Unchanged
                        TempDBContext.Core.ObjectStateManager _
                            .ChangeRelationshipState( _
                                userToProcess, _
                                ug, _
                                "UsersGroupsList", _
                                EntityState.Unchanged)
                    Else
                        TempDBContext.Entry(ug).State = EntityState.Added
                        TempDBContext.Core.ObjectStateManager _
                            .ChangeRelationshipState( _
                                userToProcess, _
                                ug, _
                                "UsersGroupsList", _
                                EntityState.Added)
                    End If
                Next
    
                ' 3. Setting objects to Unchanged, which exist in the database, but are newly related to userToProcess.
    
                For Each adg As ADGroup In UsersADGroupsToAdd
                    If adg.ID > 0 Then
                        TempDBContext.Entry(adg).State = EntityState.Unchanged
                        TempDBContext.Core.ObjectStateManager _
                            .ChangeRelationshipState( _
                                userToProcess, _
                                adg, _
                                "UsersADGroupsList", _
                                EntityState.Added)
                    Else
                        TempDBContext.Entry(adg).State = EntityState.Added
                        TempDBContext.Core.ObjectStateManager _
                            .ChangeRelationshipState( _
                                userToProcess, _
                                adg, _
                                "UsersADGroupsList", _
                                EntityState.Added)
                    End If
    
                Next
    
                For Each ug As Group In UsersUserGroupsToAdd
                    If ug.ID > 0 Then
                        TempDBContext.Entry(ug).State = EntityState.Unchanged
                        TempDBContext.Core.ObjectStateManager _
                            .ChangeRelationshipState( _
                                userToProcess, _
                                ug, _
                                "UsersGroupsList", _
                                EntityState.Added)
                    Else
                        TempDBContext.Entry(ug).State = EntityState.Added
                        TempDBContext.Core.ObjectStateManager _
                            .ChangeRelationshipState( _
                                userToProcess, _
                                ug, _
                                "UsersGroupsList", _
                                EntityState.Added)
                    End If
                Next
    
                ' EntityFramework adds more objects and relationships to DBContext than are contained within the navigation properties.
                ' These are to be deleted now.
                Dim TempGroupForType As Group = New Group
                Dim TempGroupType As System.Type = TempGroupForType.GetType()
                Dim TempADGroupForType As ADGroup = New ADGroup
                Dim TempADGroupType As System.Type = TempADGroupForType.GetType()
                Dim TempUserForType As User = New User
                Dim TempUserType As System.Type = TempUserForType.GetType()
    
                For Each e As System.Data.Entity.Infrastructure.DbEntityEntry(Of Modelbase) _
                    In TempDBContext.ChangeTracker.Entries(Of Modelbase)()
                    Select Case e.Entity.GetType()
                        Case TempGroupType
                            Dim TempGroup As Group = TryCast(e.Entity, Group)
                            If TempGroup IsNot Nothing _
                                AndAlso TempGroup.ID > 0 _
                                AndAlso TempGroup.ParentGroup IsNot Nothing _
                                AndAlso TempGroup.ParentGroup.ID > 0 Then
                                ' Detach relations to parent groups from DBContext.
                                TempDBContext.Core.ObjectStateManager _
                                                                .ChangeRelationshipState( _
                                                                    TempGroup, _
                                                                    TempGroup.ParentGroup, _
                                                                    "ParentGroup", _
                                                                    EntityState.Detached)
                            End If
                            If e.State = EntityState.Added _
                                    AndAlso TempGroup IsNot Nothing _
                                    AndAlso TempGroup.ID > 0 Then
                                e.State = EntityState.Detached
                            End If
                        Case TempADGroupType
                            Dim TempADGroup As ADGroup = TryCast(e.Entity, ADGroup)
                            If e.State = EntityState.Added _
                                AndAlso TempADGroup IsNot Nothing _
                                AndAlso TempADGroup.ID > 0 Then
                                e.State = EntityState.Detached
                            End If
                        Case TempUserType
                            Dim TempUser As User = TryCast(e.Entity, User)
                            If e.State = EntityState.Added _
                                AndAlso TempUser IsNot Nothing _
                                AndAlso TempUser.ID > 0 Then
                                e.State = EntityState.Detached
                            End If
                    End Select
                Next
    
                ' Save changes to database.
                TempDBContext.SaveChanges()
    
                Success = True
    
            End Using
    
        Return Success
    End Function
    
    这些对象看起来像这样。存在用户,这些用户可以属于多个组(n:m)。或者,它们可以属于AD组(n:m),并且组和AD组之间可以存在(1:1)关系

    Public Class User
        Inherits ModelBase
    
        Public Property ID() As Integer
        Public Property UserName() As String
        ' List of AD groups to which the user belongs.
        Public Property UsersADGroupsList() As List(Of ADGroup)
        ' List of groups to which the user belongs.
        Public Property UsersGroupsList() As List(Of Group)
    
        Public Sub New()
            _UsersADGroupsList = New List(Of ADGroup)
            _UsersGroupsList = New List(Of Group)
        End Sub
    
    End Class
    
    Public Class ADGroup
        Inherits ModelBase
    
        Public Property ID() As Integer
        Public Property Name() As String
        ' List of related users.
        Public Property RelatedUsersList() As List(Of User)
        ' Reference to related group.
        Public Property RelatedGroup() As Group
    
        Public Sub New()
            _RelatedUsersList = New List(Of User)
        End Sub
    
    End Class
    
    Public Class Group
        Inherits ModelBase
    
        Public Property ID() As Integer
        Public Property Name() As String
        ' List of related users.
        Public Property UserList() As List(Of User)
        ' Reference to parent group.
        Public Property ParentGroup() As Group
        ' List of children groups.
        Public Property ChildrenGroupsList() As List(Of Group)
        ' Reference to AD group.
        Public Property RelatedADGroup As ADGroup
    
        Public Sub New()
            _UserList = New List(Of User)
            _ChildrenGroupsList = New List(Of Group)
        End Sub
    
    End Class
    
    最后,DBContext:

    Public Class MyDBContext
        Inherits DBContext
    
        ' DBSets for the classes below.
        Public Property UserDBSet As DbSet(Of User)
        Public Property GroupDBSet As DbSet(Of Group)
        Public Property ADGroupDBSet As DbSet(Of ADGroup)
        ' Access to ObjectContext below DBContext.
        Private _Core As ObjectContext
        Public Property Core() As ObjectContext
            Get
                Return TryCast(Me, IObjectContextAdapter).ObjectContext
            End Get
            Private Set(ByVal value As ObjectContext)
                _Core = value
            End Set
        End Property
    
        Public Sub New()
    
        End Sub
    
        Public Sub New(ByVal nameOrConnectionString As String)
            MyBase.New(nameOrConnectionString)
        End Sub
    
        ' Controls the generation of the db model.
        Protected Overrides Sub OnModelCreating(modelBuilder As DbModelBuilder)
            modelBuilder.Configurations.Add(New ADGroupConfiguration())
            modelBuilder.Configurations.Add(New GroupConfiguration())
            modelBuilder.Configurations.Add(New UserConfiguration())
        End Sub
    
    End Class
    
    Public Class UserConfiguration
        Inherits EntityTypeConfiguration(Of User)
    
        Public Sub New()
            ' Defining optional and required properties.
            [Property](Function(u) u.UserName).IsRequired()
    
            ' Defining n:m-relationships.
            HasMany(Function(u) u.UsersGroupsList).WithMany(Function(g) g.UserList)
        End Sub
    
    End Class
    
    Public Class ADGroupConfiguration
        Inherits EntityTypeConfiguration(Of ADGroup)
    
        Public Sub New()
            ' n:m-relationships.
            HasMany(Function(a) a.RelatedUsersList).WithMany(Function(u) u.UsersADGroupsList)
        End Sub
    
    End Class
    
    Public Class GroupConfiguration
        Inherits EntityTypeConfiguration(Of Group)
    
        Public Sub New()
            ' Defining 1:1-relationships.
            HasOptional(Function(g) g.RelatedADGroup).WithOptionalDependent(Function(a) a.RelatedGroup)
    
            ' Defining n:m-relationships.
            HasMany(Function(g) g.UserList).WithMany(Function(u) u.UsersGroupsList).Map(Sub(t)
                                                                                            t.ToTable("UsersGroups")
                                                                                        End Sub)
        End Sub
    
    End Class
    
    我使用的是实体框架6.0.1和Visual Studio 2013

    对我来说,出现了以下问题:

  • 更新对象(图形)时我做错了什么
  • 我的解决方案虽然受到Lerman&Miller(2012,编程实体框架:DBContext)的启发,但在我看来过于复杂了。如何使它更简单
  • 如何处理异常?我假设会出现乐观并发异常,但我不知道如何处理它们,因为异常不会返回对象,这导致了异常。由于独立协会
  • 我不熟悉实体框架。所以我的问题有可能有一个简单的解决方案

    先谢谢你