Vb.net 具有多对多关系的MVC4控制器
我有两个实体,竞技场和监管机构,它们之间有着多对多的关系。我已经实现了似乎是EF代码第一个被接受的解决方案(见下文) 我现在一直致力于实现控制器视图,因此当用户创建一个调节器时,他可以选择一个或多个竞技场(可能带有复选框或多选列表),当他们创建竞技场时,可以选择一个或多个调节器 MVC4是否有办法为我生成控制器和视图,就像一对多关系一样 编辑:从最初的评论中,我现在了解到我可以将所选竞技场添加到调节器对象的竞技场导航属性中。我无法找到将选择列表添加到编辑(和创建)视图,然后在控制器中进行更改的方法。有人能举个例子吗 EDIT2:我有编辑操作的代码,如果EF确实更新了关系(regulator.ArenaIDs是我添加到regulator类的整数列表,用于从MultiSelectList中获取所选项目ID),那么编辑操作应该可以工作: 使用以下DbContextVb.net 具有多对多关系的MVC4控制器,vb.net,entity-framework,asp.net-mvc-4,Vb.net,Entity Framework,Asp.net Mvc 4,我有两个实体,竞技场和监管机构,它们之间有着多对多的关系。我已经实现了似乎是EF代码第一个被接受的解决方案(见下文) 我现在一直致力于实现控制器视图,因此当用户创建一个调节器时,他可以选择一个或多个竞技场(可能带有复选框或多选列表),当他们创建竞技场时,可以选择一个或多个调节器 MVC4是否有办法为我生成控制器和视图,就像一对多关系一样 编辑:从最初的评论中,我现在了解到我可以将所选竞技场添加到调节器对象的竞技场导航属性中。我无法找到将选择列表添加到编辑(和创建)视图,然后在控制器中进行更改的方
Public Class TslilContext
Inherits DbContext
Public Property Arenas As DbSet(Of Arena)
Public Property Regulators As DbSet(Of Regulator)
Protected Overrides Sub OnModelCreating(ByVal modelBuilder As DbModelBuilder)
modelBuilder.Entity(Of Arena)(). _
HasMany(Function(c) c.Regulators). _
WithMany(Function(p) p.Arenas). _
Map(Function(m)
m.MapLeftKey("ArenaId")
m.MapRightKey("RegulatorId")
m.ToTable("Tiers")
End Function)
End Sub
经过大量研究,我了解到EF无法更新人际关系-非常令人惊讶和失望。另外,建议的解决方案是手动更新联接表和导航属性——这不是很好。NHibernate显然是开箱即用,我完全打算在下次需要时进行调查 幸运的是,我从中找到了一个非常好的解决方案,它为DbContext添加了一个扩展方法,允许自动更新复杂的关系。甚至还有一个Nuget软件包 下面是我的完整解决方案: 我在Regulator类中添加了一个整数列表,它获取所选竞技场的ID
Public Class Regulator
Public Property Id As Integer
Public Property Name As String
Public Property ArenaIDs() As ICollection(Of Integer)
Public Overridable Property Arenas() As ICollection(Of Arena)
End Class
在“获取编辑”操作中,将处理此问题并创建一个MultiSelectList:
' GET: /Regulator/Edit/5
Function Edit(Optional ByVal id As Integer = Nothing) As ActionResult
Dim regulator As Regulator = db.Regulators.Find(id)
If IsNothing(regulator) Then
Return HttpNotFound()
End If
For Each a In regulator.Arenas
regulator.ArenaIDs.Add(a.Id)
Next
ViewBag.MultiSelectArenas = New MultiSelectList(db.Arenas.ToList(), "Id", "Name", regulator.ArenaIDs)
Return View(regulator)
End Function
并且在视图中使用MultiSelectList:
<div class="editor-field">
@Html.ListBoxFor(Function(m) m.ArenaIDs, ViewBag.MultiSelectArenas)
@Html.ValidationMessageFor(Function(model) model.Arenas)
</div>
@Html.ListBoxFor(函数(m)m.ArenaIDs,ViewBag.MultiSelectArenas)
@Html.ValidationMessageFor(函数(模型)model.Arenas)
在后期编辑操作中,将检索选择ID并用于更新竞技场集合。然后,UpdateGraph扩展方法带来了魔力,它完成了EF无法完成的事情,并更新了关系
' POST: /Regulator/Edit/5
<HttpPost()> _
<ValidateAntiForgeryToken()> _
Function Edit(ByVal regulator As Regulator) As ActionResult
If ModelState.IsValid Then
For Each i In regulator.ArenaIDs
regulator.Arenas.Add(db.Arenas.Find(i))
Next
db.UpdateGraph(Of Regulator)(regulator, Function(map) map.AssociatedCollection(Function(r) r.Arenas))
db.SaveChanges()
Return RedirectToAction("Index")
End If
“POST:/Regulator/Edit/5
_
_
功能编辑(ByVal调节器作为调节器)作为操作结果
如果ModelState.IsValid,则
对于调节器中的每个i.aids
regulator.Arenas.Add(db.Arenas.Find(i))
下一个
db.UpdateGraph(调节器)(调节器、功能(地图)地图相关集合(功能(r)r.Arenas))
db.SaveChanges()
返回重定向操作(“索引”)
如果结束
这里的问题不是EF不更新关系,而是它不会自动更新关系
您需要做的是为监管机构检索当前竞技场,然后浏览列表并删除不在新列表中的任何现有条目。然后您需要添加任何不存在的条目。然后保存更改
这确实有效,但你的问题是,你要么盲目地添加可能已经存在的关系,要么试图更新不存在的关系。您必须实际获取现有列表,并确定要添加或删除哪些关系
我知道你已经找到了一个适合你的解决方案,我的观点是,如果你试图做一个严格的基于EF的解决方案,那么你就走错了方向
我想另一种选择是删除所有关系,保存更改,然后添加新的集合并再次保存更改。如果存在重叠,这将删除一些已经存在的内容,但这将是相当直接和简单的
另一个选项是删除现有的,重新添加它们,然后遍历原始集合,并将以前存在的任何集合的状态更改为“已修改”或“无”。更多的参与,但只需要保存。我对EF没有管理多对多关系有同样的问题。我不同意Mystere Man提供的解决方案。我首先使用EF代码对所有集合更新使用RefactorThis.GraphDiff。但说它不正确是不公平的
就像GilShalit提到的,nhibernate处理得非常出色。这让我觉得,EF不是完全开发的产品。我们的工作不是获取所有集合并检查添加/删除所有这些内容。看起来很恶心。添加集合时,EF应处理要添加的集合和要删除的集合。谢天谢地,RefactorThis.GraphDiff目前正在发挥作用。我更喜欢nhibernate而不是ef。很遗憾,我需要在当前项目中使用EF。您不需要访问联接表。您有导航属性来读取现有关系并添加新关系。您有数据库集来列出现有的实体。不需要更多。对于控制器。。。您必须自己编写,但如何更新联接表?框架会这样做吗?为什么需要访问中间表?您无法执行哪种操作?@GilShalit EF将在添加/更改导航属性时更新联接表。如果您根据这些评论将答案更改为更具体,那么我们可能会向您展示一个示例。我对问题进行了编辑,以反映最初的评论。
<div class="editor-field">
@Html.ListBoxFor(Function(m) m.ArenaIDs, ViewBag.MultiSelectArenas)
@Html.ValidationMessageFor(Function(model) model.Arenas)
</div>
' POST: /Regulator/Edit/5
<HttpPost()> _
<ValidateAntiForgeryToken()> _
Function Edit(ByVal regulator As Regulator) As ActionResult
If ModelState.IsValid Then
For Each i In regulator.ArenaIDs
regulator.Arenas.Add(db.Arenas.Find(i))
Next
db.UpdateGraph(Of Regulator)(regulator, Function(map) map.AssociatedCollection(Function(r) r.Arenas))
db.SaveChanges()
Return RedirectToAction("Index")
End If