如何处理MVC中的2对1关系

本文关键字:中的 关系 MVC 何处理 处理 | 更新日期: 2023-09-27 18:16:29

所以,在我的模型中,我有一个玩家类和一个游戏类,游戏有2个需要的玩家,像这样:(连同一些其他字段我没有包括在这里)

public class Game
{
    public Player Player1 { get; set; }
    [Required]
    public int Player1Id { get; set; }
    public Player Player2 { get; set; }
    [Required]
    public int Player2Id { get; set; }
}

当尝试将此模型迁移到数据库时,我得到以下错误:

引入外键约束FK_dbo.Games_dbo。表"Games"上的Players_Player2Id可能导致循环或多个级联路径。指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他外键约束。

(这个stackoverflow问题的答案很好地解释了为什么我得到这个错误。)

为了避免这个错误,我添加了以下流畅的API代码:
modelBuilder.Entity<Game>()
        .HasRequired(c => c.Player1)
        .WithMany()
        .HasForeignKey(c => c.Player1Id)
        .WillCascadeOnDelete(false);
modelBuilder.Entity<Game>()
       .HasRequired(c => c.Player2)
       .WithMany()
       .HasForeignKey(c => c.Player2Id)
       .WillCascadeOnDelete(false);

我现在得到的问题是,然而,当我尝试删除一个球员,其Id在一个或多个游戏中被引用。

操作失败:无法更改关系,因为一个或多个外键属性不可为空。当对关系进行更改时,相关的外键属性被设置为空值。如果外键不支持空值,则必须定义一个新的关系,必须为外键属性分配另一个非空值,或者必须删除不相关的对象。

如果我正确理解了这个错误,我得到这个错误是因为游戏失败的一个外键约束(级联删除是假的,所以游戏不会随着玩家一起被删除。相反,Player1Id(或Player2Id)被设置为null,这打破了约束,因为两者都是Required和non-nullable)

所以我试着在Seed()方法中创建一个像这个So问题一样的触发器。

context.Database.ExecuteSqlCommand("CREATE TRIGGER Game_Player_Del on dbo.Players instead of delete as " +
            "set nocount on " +
            "delete from dbo.Games where Player1Id in (select Id from deleted) or " +
            "Player2Id in (Select Id from deleted) " + 
            "delete from dbo.Players where Id in (select Id from deleted)");

当我检查我的数据库时,触发器确实被添加,但它似乎不起作用,因为我仍然得到与添加触发器之前相同的错误。

那么,我该怎么做才能使它按预期工作呢?(删除玩家也会删除相关的游戏,删除游戏不会删除玩家)

编辑:我刚想到的一个可能的解决方案是编辑我的控制器中的删除方法,首先获得所有游戏,其中Player1Id或Player2Id等于Player。Id,删除那些,然后删除玩家,但(我认为)我不应该在删除玩家时担心游戏,实体框架应该为我做这些。

编辑2:(回应用户的评论)这是我的删除代码:

控制器:

[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
    repo.deletePlayer(id);
    return RedirectToAction("Index");
}
回购:

public void deletePlayer(int id)
{
    using (var context = new TennisContext())
    {
        player = context.Players.Find(id);
        context.Entry(player).State = EntityState.Deleted;
        context.SaveChanges();
    }
}

编辑3:在删除播放器之前,我通过查询和删除受影响的游戏来使其工作。

回购代码:

public void deletePlayer(int id)
{
    using (var context = new TennisContext())
    {
        player = context.Players.Find(id);
        List<Game> playerGames = context.Games.Where(g => g.Player1Id == id || g.Player2Id == id).ToList();
        foreach (Game g in playerGames)
        {
            context.Entry(g).State = EntityState.Deleted;
        }
        context.Entry(player).State = EntityState.Deleted;
        context.SaveChanges();
    }
}

如何处理MVC中的2对1关系

使用实体框架代码手动删除游戏。查询受影响的游戏并将其传递给适当的删除方法(例如Entry(player).State = EntityState.Deleted或任何其他等效的东西)。

由于无法使用自动级联,所以请手动执行

两个球员的id都有[Required]属性如果你试图删除一个球员会发生什么?也许其中一个玩家应该是空的?