【发布时间】:2017-03-19 22:54:45
【问题描述】:
因此,在我的模型中,我有一个 Player 类和一个 Game 类,Game 有 2 个必需的 Player,如下所示:(以及我未在此处包含的其他一些字段)
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; }
}
尝试将此模型迁移到数据库时,我收到以下错误:
在表“Games”上引入 FOREIGN KEY 约束“FK_dbo.Games_dbo.Players_Player2Id”可能会导致循环或多个级联路径。指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN KEY 约束。
(this stackoverflow question 的答案很好地解释了我为什么会收到此错误。)
为了避免这个错误,我添加了以下流畅的 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,这打破了约束,因为两者都是必需且不可为 null)
所以我尝试在 Seed() 方法中创建类似 this SO question 的触发器。
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:(回应usr的评论)这是我的删除代码:
控制器:
[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();
}
}
【问题讨论】:
-
触发器不是正确的解决方案。这可以在 EF 级别解决。 (我不知道如何。)发布您的删除代码。
-
@usr 我添加了我的删除代码
-
有一个 Player 属性,它是 Player 对象的集合。验证该集合至少有两个。不确定是否有 ValidationAtttibute 可以为您完成,但您可以轻松创建一个。
-
使外键可以为空。
标签: c# entity-framework asp.net-mvc-5