【问题标题】:MVC4 controller with Many to Many relationship具有多对多关系的 MVC4 控制器
【发布时间】:2013-07-03 08:31:35
【问题描述】:

我有两个实体,Arena 和 Regulator,它们之间有多对多的关系。我已经实现了似乎是 EF 代码首先接受的解决方案(见下文)。

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

有没有办法让 MVC4 为我生成控制器和视图,就像它为一对多关系所做的那样?

编辑:从最初的 cmets 开始,我现在了解到我可以将选定的竞技场添加到调节器对象的 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

【问题讨论】:

  • 您不需要访问连接表。您有导航属性来读取现有关系并添加新关系。你有 DbSet(s) 来列出现有的实体。不需要更多。对于控制器......你将不得不自己编写它
  • 但是连接表将如何更新呢?框架会这样做吗?
  • 为什么需要访问中间表?您无法执行哪些操作?
  • @GilShalit EF 将在您添加/更改导航属性时更新连接表。如果您根据这些 cmets 将答案更改为更具体,那么也许我们可以向您展示一个示例。
  • 我已编辑问题以反映最初的 cmets。

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


【解决方案1】:

经过大量研究,我了解到 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

并且在View中使用了MultiSelectList:

    <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

【讨论】:

    【解决方案2】:

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

    您需要做的是为监管者检索当前的竞技场,然后遍历列表并删除新列表中不存在的所有条目。然后,您需要添加任何尚不存在的条目。然后保存更改。

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

    我知道您已经找到了适合您的解决方案,我的意思是,如果您尝试做一个严格的基于 EF 的解决方案,那么您就走错了路。

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

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

    【讨论】:

      【解决方案3】:

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

      就像 GilShalit 提到的那样,nhibernate 处理得非常出色。这让我跌倒了,EF 不是完全开发的产品。获取所有集合并检查添加/删除所有这些东西不是我们的工作。它看起来非常讨厌。添加集合时,EF 应处理要添加的集合和要删除的集合。值得庆幸的是 RefactorThis.GraphDiff 暂时有所帮助。我更喜欢休眠而不是ef。不幸的是,我需要在当前项目中使用 EF。

      【讨论】:

        猜你喜欢
        • 2018-06-11
        • 1970-01-01
        • 1970-01-01
        • 2014-06-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多