【问题标题】:Repository pattern with Entity Framework 5. Updating related objects实体框架的存储库模式 5. 更新相关对象
【发布时间】:2013-09-05 14:09:59
【问题描述】:

我正在为使用 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

问题

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

更新后的新对象

SetValues 之后的实体

从最初的研究来看,SetValues 方法似乎不会复制this post 中讨论的相关导航属性

问题

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

补充说明

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

【问题讨论】:

    标签: .net vb.net entity-framework .net-4.5 repository-pattern


    【解决方案1】:

    我发现 Entity Framework 不支持这一点非常令人惊讶,但像往常一样,社区已经通过RefactorThis' GraphDiff 进行救援,可以通过their GitHub repository 下载

    看起来实体框架团队将在 EF6 之后将此功能视为其当前编号 2 on there issue list,而 Rowan Miller 似乎认为这是个好主意

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

    EF 团队分类:我们同意这将是一个很好的启用方案。 考虑到我们在 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()
    

    【讨论】:

      【解决方案2】:

      基本上,Entity Framework 缺乏这样做的能力。 或者说得更好,它缺乏适当的优化方法。

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

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

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

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

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

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-11-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-25
        • 2010-12-11
        相关资源
        最近更新 更多