MVC4控制器具有多对多的关系

时间:2013-07-03 08:31:35

标签: vb.net entity-framework asp.net-mvc-4

我有两个实体,竞技场和监管机构,它们之间有很多关系。我已经实现了似乎是EF代码首次接受的解决方案(见下文)。

我现在无法实现控制器视图,因此当用户创建调节器时,他可以选择一个或多个竞技场(可能带有复选框或多选列表),当他们创建竞技场时,一个或多个监管机构可以选择。

MVC4是否有办法为我生成控制器和视图,就像它对一对多关系一样?

编辑:从最初的评论,我现在明白我可以将选定的竞技场添加到监管对象的Arenas导航属性中。我无法找到将选择列表添加到编辑(和创建)视图的方法,然后在控制器中进行更改。任何人都可以提供一个例子吗?

EDIT2:我有编辑操作的代码,如果EF确实更新了关系,那么它应该有效(regulator.ArenaIDs是我添加到调节器类的整数列表,用于从MultiSelectList中获取所选项IDS):

<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.Entry(regulator).State = EntityState.Modified
        db.SaveChanges()
        Return RedirectToAction("Index")
    End If

    Return View(regulator)
End Function

我正在使用VS 2012和EF 5.0

这是我的实施:

Public Class Arena
    Public Property Id As Integer
    Public Property Name As String
    Public Overridable Property Regulators() As ICollection(Of Regulator)
End Class

Public Class Regulator
    Public Property Id As Integer
    Public Property Name As String
    Public Overridable Property Arenas() As ICollection(Of Arena)
End Class

使用以下DbContext

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

3 个答案:

答案 0 :(得分:2)

经过大量研究后,我了解EF无法更新关系 - 非常令人惊讶和失望。显然,建议的解决方案是手动更新连接表和导航属性 - 不是很好。 NHibernate显然是开箱即用的,我完全打算在下次需要时进行调查。

幸运的是,我遇到了一个来自Refactor(This)的真正优秀的解决方案,它为DbContext添加了一个扩展方法,允许自动更新复杂的关系。甚至还有一个Nuget套餐!

所以这是我的完整解决方案:

我在Regulator类中添加了一个整数列表,它获取所选Arenas的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

在GET Edit操作中,这将处理并创建一个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用于View:

    <div class="editor-field">
        @Html.ListBoxFor(Function(m) m.ArenaIDs, ViewBag.MultiSelectArenas)
        @Html.ValidationMessageFor(Function(model) model.Arenas)
    </div>

在POST Edit操作中,将检索选择ID并用于更新Arenas集合。然后使用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

答案 1 :(得分:1)

这里的问题不是EF不会更新关系,而是自动这样做。

您需要做的是检索调节器的当前Arenas,然后遍历列表并删除新列表中不存在的任何条目。然后,您需要添加任何尚不存在的条目。然后SaveChanges。

这确实有效,但您的问题是您要么盲目地添加可能已经存在的关系,要么尝试更新那些不存在的关系。您必须实际获取现有列表并找出要添加或删除的关系。

我知道你已经找到了一个适合你的解决方案,我的观点是,如果你试图做一个严格的基于EF的解决方案,那么你就是以错误的方式去做。

我想另一个选择是删除所有关系,SaveChanges,然后再次添加新集和SaveChanges。如果存在重叠,这将删除已经存在的一些,但是相当简单直接。

另一个选项是删除现有的,重新添加它们,然后遍历原始集并将之前存在的任何状态更改为Modified或None。更多涉及,但只需要保存。

答案 2 :(得分:0)

我对EF没有管理多对多关系存在同样的问题。我不同意Mystere Man提供的解决方案。我使用RefactorThis.GraphDiff使用EF Code First进行所有集合更新。但说它不正确是不公平的。

就像GilShalit所提到的那样,nhibernate可以很好地处理它。它让我摔倒,EF还没有完全开发出来的产品。获取所有集合并检查添加/删除所有这些内容不是我们的工作。看起来很讨厌。在添加集合时,EF应该处理要添加的集合和要删除的集合。值得庆幸的是RefactorThis.GraphDiff暂时在帮忙。我更喜欢nhibernate而不是ef。不幸的是,我需要在当前项目中使用EF。