2

I have the two entities, Arena and Regulator, which have a many to many relationship between them. I have implemented what seems to be the EF code first accepted solution (see below).

I am now stuck with implementing the controller views, so that when a user creates a regulator, he can select one or more arenas (probably with check boxes or multi select list), and when they create an arena, one or more regulators can be selected.

Is there a way for MVC4 to generate the controller and views for me, like it does for one to many relationships?

EDIT: From the initial comments, I now understand that I can add the selected arena to the Arenas navigation property of the regulator object. I have not been able to find the way to both add the selection list to the Edit (and Create) views, and then make the changes in the controller. Can anyone supply an example?

EDIT2: I have code for the Edit actions that should work if EF did indeed update relationships (regulator.ArenaIDs is a list of integers I added to the regulator class, to get the selected item IDS from the MultiSelectList):

<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

I am using VS 2012 and EF 5.0

Here is my implementation:

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

with the following 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
4

3 回答 3

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

并且在 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
于 2013-07-05T19:09:06.500 回答
1

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

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

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

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

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

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

于 2013-07-05T21:44:55.793 回答
0

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

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

于 2013-09-04T14:56:12.333 回答