在 Entity Framework 中编辑多对多。可以添加但不能删除

Editing Many to Many in Entity Framework. Can add but not remove

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

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

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

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);
}

型号class是

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);
}

你可以试试这个...

[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将从游戏中删除不再选择的平台。它将保留仍处于选中状态的平台,并且还会向已选中的游戏添加新平台。

解决方案:使用 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);
}