【问题标题】:Editing Many to Many in Entity Framework. Can add but not remove在实体框架中编辑多对多。可以添加但不能删除
【发布时间】:2016-01-23 21:08:57
【问题描述】:

在我的 POST 编辑函数中,我的视图模型包含我想要更新的游戏和我想要添加到游戏中的平台 ID 列表。

使用此代码,我能够将平台添加到我的游戏中,但无法删除它们。我在最后放了一个断点,肯定看到 viewModel.Game.Platforms 只有我选择的内容,但它没有在我的游戏列表中更新。

如果我添加几个平台并同时删除一些。添加了新平台,但没有删除任何平台。

public ActionResult Edit(GameViewModel viewModel)
{
    if (ModelState.IsValid)
    {
        List<Platform> platforms = new List<Platform>();
        foreach (var id in viewModel.PostedPlatforms.PlatformIds)
        {
            platforms.Add(db.Platforms.Find(Int32.Parse(id)));
        }
        db.Games.Attach(viewModel.Game);
        viewModel.Game.Platforms = platforms;
        db.Entry(viewModel.Game).State = EntityState.Modified;
        UpdateModel(viewModel.Game);
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(viewModel.Game);
}

模型类是

public class Game
{
    public int GameId { get; set; }

    public string Title { get; set; }

    public List<Platform> Platforms { get; set; }
}

public class Platform
{
    public int PlatformId { get; set; }

    public string Name { get; set; }

    public List<Game> Games { get; set; }
}

根据 ourmandave 的建议,我得到了这段代码,虽然确实改变了平台选择,但它每次都会创建一个新的游戏条目,效率低下,并且还会增加内容的 id,从而弄乱书签。

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(GameViewModel viewModel)
{
    if (ModelState.IsValid)
    {
        List<Platform> platforms = new List<Platform>();
        if(viewModel.PostedPlatforms != null)
        {
            foreach (var id in viewModel.PostedPlatforms.PlatformIds)
            {
                platforms.Add(db.Platforms.Find(Int32.Parse(id)));
            }
        }
        db.Games.Remove(db.Games.Find(viewModel.Game.PostId));
        db.SaveChanges();
        viewModel.Game.Platforms = platforms;
        db.Games.Add(viewModel.Game);

        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(viewModel.Game);
}

【问题讨论】:

  • 删除项目的代码在哪里?
  • 在我的编辑功能中,我从我选择的所有平台创建一个新的 List 并将 Game.Platforms 设置为那个。新列表没有 Game.Platforms 中的某些平台。这不应该意味着这些平台不再存在于 Game.Platforms 中吗?
  • 我认为您必须在它们上调用 Remove 或 DeleteObject 以在上下文中标记它们,因此当您 SaveChanges 时它将生成 Delete sql per this answer。另请参阅此页面上的Entity states and SaveChanges
  • 我没有看到仅在我的游戏平台列表中调用它的方法。如果我必须删除我的游戏并在每次我想更新它时重新添加它,这似乎相当尴尬。

标签: c# asp.net-mvc entity-framework entity-framework-5


【解决方案1】:

你可以试试这个……

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(GameViewModel viewModel)
{
    if (ModelState.IsValid)
    {
        List<Platform> selectedPlatforms = viewModel.Select(pl => GetPlatformById(pl.Id)).ToList();
        var game = GetGameById(viewModel.Id);

        UpdateGamePlatforms(game, selectedPlatforms);

        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(viewModel.Game);
    }        



private Platform GetPlatformById(int platformId)
{
    return db.Platforms.First(pl => pl.Id == platformId);
}

private Game GetGameById(int gameId)
{
    return db.Games.First(g => g.Id == gameId);
}

private void UpdateGamePlatforms(Game game, IList<Platform> selectedPlatforms)
{
    var gamePlatforms = game.Platforms.ToList();

    foreach (var gamePlatform in gamePlatforms)
    {
        if (selectedPlatforms.Contains(gamePlatform) == false)
        {
            game.Platforms.Remove(gamePlatform);
        }
        else
        {
            selectedPlatforms.Remove(gamePlatform);
        }
    }

    game.Platforms.AddRange(selectedPlatforms);
}

UpdateGamePlatforms将从游戏中移除不再被选中的平台。它将离开仍被选中的平台,并且还会将新的平台添加到已选中的游戏中。

【讨论】:

  • 谢谢。那工作完美。我只将第一部分更改为selectedPlatforms.AddRange(db.Platforms.Where(item =&gt; platformIds.Contains(item.PlatformId)).ToList());,因为我有一个选定平台的 id 数组。
  • 经过进一步测试,我注意到您的代码不会考虑对平台之外的其他游戏属性的任何更改。我尝试使用我的 viewModel.Game 而不是创建一个新的 Game 对象并添加 db.Entry(viewModel.Game).State = EntityState.Modified; 我可以更新其他属性但不能更新平台。除了手动为每个属性执行game.Title = viewModel.Game.Title 之类的操作之外,还有其他方法吗?
  • 我就是这样做的。您可以将其拉出到单独的方法中...private void Map(Game game, GameViewModel viewModel) 然后在此方法中您可以从 GameViewModel 设置游戏的所有其他属性。
【解决方案2】:

解决方案:使用 TomJerrum 的解决方案,我现在可以正确编辑平台列表。要更新其余属性,我必须映射我正在尝试编辑的游戏对象的所有属性以匹配我的 viewModel 的属性。谢天谢地,已经有一个功能,所以我只需要添加 db.Entry(game).CurrentValues.SetValues(viewModel.game);.

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(GameViewModel viewModel)
{
    if (ModelState.IsValid)
    {
        List<Platform> selectedPlatforms = new List<Platform>();

        if (viewModel.PostedPlatforms != null)
        {
            int[] platformIds = Array.ConvertAll(viewModel.PostedPlatforms.PlatformIds, p => Convert.ToInt32(p));

            selectedPlatforms.AddRange(db.Platforms.Where(item => platformIds.Contains(item.PlatformId)).ToList());
        }

        var game = GetGameById(viewModel.Id);

        UpdateGamePlatforms(game, selectedPlatforms);

        db.Entry(game).CurrentValues.SetValues(viewModel.game);

        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(viewModel.Review);
}

private Game GetGameById(int gameId)
{
    return db.Games.First(g => g.Id == gameId);
}

private void UpdateGamePlatforms(Game game, IList<Platform> selectedPlatforms)
{
    var gamePlatforms = game.Platforms.ToList();

    foreach (var gamePlatform in gamePlatforms)
    {
        if (selectedPlatforms.Contains(gamePlatform) == false)
        {
            game.Platforms.Remove(gamePlatform);
        }
        else
        {
            selectedPlatforms.Remove(gamePlatform);
        }
    }

    game.Platforms.AddRange(selectedPlatforms);
}

【讨论】:

    猜你喜欢
    • 2011-10-08
    • 2022-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多