2

我正在为使用 Entity Framework 5 作为后端数据源的新应用程序构建存储库。我有适用于简单模型的基本 CRUD 操作,但我很难理解如何在更新现有对象时更新相关对象。

模型

在此处输入图像描述

导入系统导入 System.Collections.Generic

Partial Public Class tblUser
    Public Property idUser As Integer
    Public Property username As String
    Public Property pwd As String

    Public Overridable Property tblUsermmRoles As ICollection(Of tblUsermmRole) = New HashSet(Of tblUsermmRole)
End Class

Imports System
Imports System.Collections.Generic

Partial Public Class tblUsermmRole
    Public Property idUser As Integer
    Public Property idRole As Integer

    Public Overridable Property tblUser As tblUser
    Public Overridable Property tblUserRole As tblUserRole

End Class

我正在尝试针对相关的 tblUser 记录将记录添加到 tblUsermmRole 对象,但我无法使更新正常工作。

POCO

Public Class User

    Public Property ID As Int32
    Public Property Username As String
    Public Property Password As String
    Public Property AccessRoles As IEnumerable(Of Int32)

    Public Sub New()
    End Sub

    Public Sub New(id As Int32, userName As String, password As String, roles As List(Of Int32))
        Me.ID = id
        Me.Username = userName
        Me.Password = password
        Me.AccessRoles = roles
    End Sub

End Class

Public Class UserRoles

    Public Property RoleID As Int32

    Public Sub New(roleID As Int32)
        Me.RoleID = roleID
    End Sub

End Class

存储库

Imports System.Data.Entity

Namespace DataAccess.Repository

    Public MustInherit Class EntityFramworkContextBase
        Inherits DbContext
        Implements IUnitOfWork

        Public Sub New(entityConnectionStringOrName As String)
            MyBase.New(entityConnectionStringOrName)
        End Sub

        Public Sub Add(Of T As Class)(obj As T) Implements IUnitOfWork.Add
            [Set](Of T).Add(obj)
        End Sub

        Public Sub Attach(Of T As Class)(obj As T) Implements IUnitOfWork.Attach

            Dim entity As T

            If ExistsInContext(obj) Then
                entity = ObjectInContext(obj)
                Entry(entity).CurrentValues.SetValues(obj)

            Else
                entity = [Set](Of T).Attach(obj)
            End If

            Entry(entity).State = EntityState.Modified

        End Sub

        Public Sub Commit() Implements IUnitOfWork.Commit
            MyBase.SaveChanges()
        End Sub

        Public Function [Get](Of T As Class)() As IQueryable(Of T) Implements IUnitOfWork.Get
            Return [Set](Of T)()
        End Function

        Public Function Remove(Of T As Class)(obj As T) As Boolean Implements IUnitOfWork.Remove

            Dim entity As T

            If ExistsInContext(obj) Then
                entity = ObjectInContext(obj)
            Else
                entity = [Set](Of T).Attach(obj)
            End If

            [Set](Of T).Remove(entity)

            Return True

        End Function

        Private Function ExistsInContext(Of T As Class)(obj As T) As Boolean
            Return [Set](Of T).Local.Any(Function(o) o.Equals(obj))
        End Function

        Private Function ObjectInContext(Of T As Class)(obj As T) As T
            Return [Set](Of T).Local.FirstOrDefault(Function(o) o.Equals(obj))
        End Function

    End Class

End Namespace

问题

在方法中调用该Entry(entity).CurrentValues.SetValues(obj)行时,Attach会复制基本属性,但不会复制 tblUsermmRole 对象的新元素。

更新后的新对象

在此处输入图像描述

SetValues 之后的实体

在此处输入图像描述

从最初的研究来看,该方法似乎没有复制本文中讨论SetValues相关导航属性

问题

鉴于我正在使用的存储库模式(加上 UnitOfWork 模式),您如何维护对象图关系并更新数据库?

附加说明

对于带有附加 tblUsermmRoles 的 tblUsers 对象的新实例,此方法按预期工作。添加两个表的记录以维护外键。

4

2 回答 2

1

我发现 Entity Framework 还不支持这一点非常令人惊讶,但像往常一样,社区已经通过RefactorThis 的 GraphDiff来拯救它,可以通过他们的 GitHub 存储库下载

看起来实体框架团队将在 EF6 之后将此功能视为其当前问题列表中的第 2 位,而 Rowan Miller 似乎认为这是一个好主意

RoMiller 写于 2 月 14 日晚上 11:15

EF Team Triage:我们同意这将是一个很好的方案。考虑到我们在 EF6 版本中的位置以及此功能的大小和影响,我们的团队不打算在 EF6 中实施它。因此,我们将其移至未来版本,以便在下一个版本中重新考虑。

同时RefactorThis 的 GraphDiff解决了大多数用例。谢谢布伦特

更新的方法如下所示

Public Sub AttachObjectGraph(Of T As Class)(obj As T, mapping As Expression(Of Func(Of IUpdateConfiguration(Of T), Object))) Implements IUnitOfWork.AttachObjectGraph
        Me.UpdateGraph(obj, mapping)
End Sub

调用方法如下所示,指定父->子实体映射

_usersRepository.AttachObjectGraph(dbUser, Function(map) map.OwnedCollection(Function(u) u.tblUsermmRoles))
_unitOfWork.Commit()
于 2013-09-06T09:30:13.040 回答
0

基本上,实体框架缺乏这样做的能力。或者更好地说,它缺乏适当的优化方式来做到这一点。

假设您有一个具有 4 个角色的用户,并且您想要删除 1 个并添加 2 个新角色。

选择用户时需要加载足够的导航属性,以便填充您的角色集合。然后使用经典的从集合中移除移除角色,并使用添加添加新角色。

一旦您点击 SaveChanges,已删除的角色将从数据库中删除(如果 1 到多个),或者将删除至少许多 2 多个关系。新角色将被保存 - m2m 关系将被更新。

对于一小组实体来说,这通常是可以的。

但是假设用户有 100.000 个朋友,并且您想从收藏中删除一个。显然加载 100.000 只是为了删除 1 是完全奇怪的,这是 Entity Framework 不是好的解决方案(或者至少我不熟悉它)。

在上述情况下,我宁愿通过同时使用用户 ID 和朋友 ID 查询直接关系来定位直接关系,而不是提取所有朋友的列表并对该集合进行操作。

还有一种更快的方法是使用 DbContext.Database.ExecuteSqlCommand 执行 ESQL 命令,以发送批量更新和删除命令。请记住,尽管实体框架没有跟踪这些更改,所以您的 DbContext 不会知道您已经更新了任何内容。

希望这个澄清至少对您有所帮助。

于 2013-09-05T16:49:12.270 回答