我正在为使用Entity Framework 5作为后端数据源的新应用程序构建存储库。我有基本的CRUD操作适用于简单的模型,但我很难理解如何在更新现有对象时更新相关对象。
模型
进口系统 Imports 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
方法似乎不会复制this post
问题
鉴于我正在使用的存储库模式(加上UnitOfWork模式)如何维护对象图关系并更新数据库?
附加说明
此方法对于附加了tblUsermmRoles的tblUsers对象的新实例的预期效果。添加两个表的记录,保持外键。
答案 0 :(得分:1)
我发现实体框架并不支持这一点,这让我感到非常惊讶,但是像往常一样,社区已经通过RefactorThis' GraphDiff来解救,可以通过their GitHub repository
下载看起来实体框架团队会将此功能视为EF6后的当前编号2 on there issue list,而Rowan Miller似乎认为这是一个好主意
RoMiller于2月14日晚上11:15写道
EF Team Triage:我们同意这是一个很好的启用方案。 考虑到我们在EF6版本中的位置以及 尺寸和我们团队不打算这个功能的影响 在EF6中实现它。因此,我们正在将其发布到Future版本 在下一个版本中重新考虑。
同时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()
答案 1 :(得分:0)
基本上,实体框架缺乏这样做的能力。 或者说它更好,它缺乏适当的优化方式。
假设您有一个具有4个角色的用户,并且您想要删除1,并添加2个新角色。
在选择用户时,您需要加载足够的导航属性,以便填充您的角色集合。然后使用经典的Remove from collection删除角色,并使用Add。
添加新角色点击SaveChanges后,已删除的角色将从数据库中删除(如果是1到多个),或者至少删除了2个以上的关系。将保存新角色 - 将更新m2m关系。
对于一小组实体来说,这通常是相当不错的。
但是假设用户有100.000个朋友,并且您想要从集合中删除一个。显然加载100.000只是为了删除1是完全奇怪的,这是实体框架不是很好的解决方案(或者至少我不熟悉它)。
在上面的场景中,我更倾向于通过用用户ID和朋友ID查询直接关系,而不是拉动所有朋友的列表并对该集合进行操作。
使用DbContext.Database.ExecuteSqlCommand执行ESQL命令还有一种更快的方法,用于发送批量更新和删除命令。请记住,虽然Entity Framework没有跟踪这些更改,因此您的DbContext将不会意识到您已更新了enything。
希望这个澄清对你至少有所帮助。