实体框架:如何在断开连接的方案中更新具有独立关联的实体?

时间:2014-05-08 18:18:15

标签: entity-framework

我正在尝试使用已在UI中更改的独立关联更新对象。加载和保存在不同的DBContexts中进行。我尝试了以下,但现在我被卡住了。我偶尔会遇到DBUpdateExceptions。

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.

据我所知,这源于现有对象之间的现有关系,这些关系由Entity Framework添加并且未包含在导航属性中,并且在绘制状态时我没有设置为Unchanged。但是如何识别它们呢?

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

  1. 将修改后的对象添加到DBContext并将其状态设置为Modified。
  2. 通过将状态设置为已删除,删除与导航属性中删除的对象(存在于数据库中)的关系。
  3. 为当前包含在导航属性中的所有对象绘制状态:将现有对象的状态(ID> 0)和关系设置为Unchanged,并将新对象的状态(ID = 0)及其关系设置为Added
  4. 绘制对象的状态,这些对象已添加到naviagtion属性中:将对象的状态设置为Unchanged(如果已存在:ID> 0)oder Added(如果new:ID = 0),以及状态与添加的关系。
  5. 从DBContext中删除所有对象和关系,这些对象和关系由实体框架添加到上下文中,但不包含在(旧的或当前的)导航属性中。
  6. 以下是代码:

    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
    

    我正在使用Entity Framework 6.0.1和Visual Studio 2013。

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

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

      提前多多感谢。

0 个答案:

没有答案