C# EF Core 5-更新多对多关系中的实体;违反主键“;错误
我正在为数据库使用和C# EF Core 5-更新多对多关系中的实体;违反主键“;错误,c#,sql-server,asp.net-core,entity-framework-core,many-to-many,C#,Sql Server,Asp.net Core,Entity Framework Core,Many To Many,我正在为数据库使用和MSSQL 我有两个类,它们之间有多对多的关系。这些已从模型迁移到数据库。 迁移: migrationBuilder.CreateTable( name: "TournamentUser", columns: table => new { TournamentsId = table.Column<int>(type: "in
MSSQL
我有两个类,它们之间有多对多的关系。这些已从模型迁移到数据库。
迁移:
migrationBuilder.CreateTable(
name: "TournamentUser",
columns: table => new
{
TournamentsId = table.Column<int>(type: "int", nullable: false),
UsersId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_TournamentUser", x => new { x.TournamentsId, x.UsersId });
table.ForeignKey(
name: "FK_TournamentUser_Tournaments_TournamentsId",
column: x => x.TournamentsId,
principalTable: "Tournaments",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_TournamentUser_Users_UsersId",
column: x => x.UsersId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
名称:“TournamentUser”,
列:表=>new
{
TournamentsId=table.Column(类型:“int”,可为空:false),
UsersId=table.Column(类型:“int”,可空:false)
},
约束:表=>
{
表.PrimaryKey(“PK_TournamentUser”,x=>new{x.TournamentsId,x.UsersId});
表1.外键(
名称:“FK_TournamentUser_Tournaments_TournamentsId”,
列:x=>x.TournamentsId,
原则:“锦标赛”,
主栏:“Id”,
onDelete:引用。级联);
表1.外键(
名称:“FK_TournamentUser_Users_UsersId”,
列:x=>x.UsersId,
原则性:“用户”,
主栏:“Id”,
onDelete:引用。级联);
});
型号:
public class Tournament
{
[Key]
public int Id { get; set; }
[Required, MaxLength(20)]
public string Name { get; set; }
[Required]
public int MaxPlayers { get; set; }
public int PlayersCount { get; set; }
[Required]
public DateTime Start { get; set; }
public bool IsStarted { get; set; }
public bool IsEnded { get; set; }
public List<User> Users { get; set; }
}
public class User
{
[Key]
public int Id { get; set; }
[Required, MaxLength(15)]
public string Username { get; set; }
[Required]
public string Password { get; set; }
[Required, MaxLength(20)]
public string Name { get; set; }
[Required, MaxLength(20)]
public string Surname { get; set; }
[JsonIgnore]
public List<Tournament> Tournaments { get; set; }
}
公开课比赛
{
[关键]
公共int Id{get;set;}
[必需,最大长度(20)]
公共字符串名称{get;set;}
[必需]
公共整数MaxPlayers{get;set;}
public int PlayersCount{get;set;}
[必需]
公共日期时间开始{get;set;}
公共布尔值已开始{get;set;}
公共布尔值为{get;set;}
公共列表用户{get;set;}
}
公共类用户
{
[关键]
公共int Id{get;set;}
[必需,最大长度(15)]
公共字符串用户名{get;set;}
[必需]
公共字符串密码{get;set;}
[必需,最大长度(20)]
公共字符串名称{get;set;}
[必需,最大长度(20)]
公共字符串姓氏{get;set;}
[JsonIgnore]
公开列表锦标赛{get;set;}
}
所以当我尝试更新锦标赛模型时
[HttpPut]
公共异步任务放置([FromBody]锦标赛)
{
_上下文。锦标赛。更新(锦标赛);
wait_context.SaveChangesAsync();
返回CreateDataAction(nameof(GetTournament),新的{id=tournament.id},tournament);
}
我得到这个错误
Microsoft.Data.SqlClient.SqlException(0x80131904):违反
主键约束“PK_TournamentUser”。无法插入重复项
输入对象“dbo.TournamentUser”。重复的键值为(1,1)
有什么可能的解决方案?我想保留我的多对多表,因为每次我更新锦标赛
时,都可以删除或添加锦标赛的用户
编辑:
下面显示了锦标赛对象数据-
如果要更新Tourment,请尝试从torment对象中删除用户
public async Task<ActionResult<Tournament>> Put([FromBody] Tournament tournament)
{
tournament.Users=null;
_context.Tournaments.Update(tournament);
await _context.SaveChangesAsync();
.....
}
解决方案:
要更新锦标赛
实体,请仅使用以下方法-
\u context.Entry(锦标赛).State=EntityState.Modified;
wait_context.SaveChangesAsync();
解释:
如果实体具有Id
(自动生成的主键)值,则Update()
方法将实体标记为Modified
,如果实体没有Id
值,则标记为Added
。如果使用实体图(具有相关实体的实体)调用该方法,则任何相关实体也是如此。然后,在下一次SaveChanges()
调用中,EF根据实体是否标记为Modified
或Added
,为实体生成更新和/或插入命令。关键是,Update()
方法不仅更新实体,还可以插入新实体
在您的案例中,通过查看模型,EF可以判断锦标赛
和用户
处于多对多关系中,并且在数据库级别存在一个加入实体锦标赛用户
。但由于您从外部接收实体图,EF不会跟踪它们,并且它无法判断此锦标赛的TournamentUser
实体以及相关用户是否已存在于数据库中
使用实体图调用Update()
方法时,锦标赛、相关用户以及与其相关的加入实体(TournamentUser
)都将成为更新操作的主题。EF尝试创建连接实体,就像创建/插入没有Id
值的新实体一样
EF为锦标赛用户
实体生成插入命令,并为锦标赛
和用户
实体生成更新命令。但是在数据库级别,由于TournamentUser
链接已经存在,您会得到违反主键的错误
-
\u context.Entry(锦标赛).State=EntityState.Modified;
上述建议仅将锦标赛明确标记为已修改
。因此,EF不必查看任何相关实体,也不必推断任何关系,只为锦标赛生成更新命令。我从中找到了解决方案
我对代码做了一些修改,因为最初它是从表中删除用户,我得到了我想要的结果
public async Task<ActionResult<Tournament>> Put([FromBody] Tournament tournament)
{
var _tournament = _context.Tournaments.Include(t => t.Users).FirstOrDefault(t => t.Id == tournament.Id);
_context.Entry(_tournament).CurrentValues.SetValues(tournament);
var _users = _tournament.Users.ToList();
// Adds new Users
foreach (var tournamentUser in tournament.Users)
{
if (_users.All(i => i.Id != tournamentUser.Id))
{
_tournament.Users.Add(tournamentUser);
}
}
// Removes old Users
foreach (var tournamentUser in _users)
{
if (tournament.Users.FirstOrDefault(tu => tu.Id == tournamentUser.Id) == null)
{
_tournament.Users.Remove(tournamentUser);
}
}
await _context.SaveChangesAsync();
return CreatedAtAction(nameof(GetTournament), new { id = tournament.Id }, tournament);
}
public异步任务Put([FromBody]锦标赛)
{
var\u tournament=\u context.Tournaments.Include(t=>t.Users).FirstOrDefault(t=>t.Id==tournament.Id);
_context.Entry(_锦标赛).CurrentValues.SetValues(锦标赛);
var_users=_tournament.users.ToList();
//添加新用户
foreach(锦标赛中的var tournamentUser.Users)
var existing = _context
.Tournaments
.Include(u=>u.Users)
.FirstOrDefault(t=> t.Id== tournament.Id);
if(existing==null) return ...error
_context.Entry(existing).CurrentValues.SetValues(tournment);
_context.SaveChanges();
public async Task<ActionResult<Tournament>> Put([FromBody] Tournament tournament)
{
var _tournament = _context.Tournaments.Include(t => t.Users).FirstOrDefault(t => t.Id == tournament.Id);
_context.Entry(_tournament).CurrentValues.SetValues(tournament);
var _users = _tournament.Users.ToList();
// Adds new Users
foreach (var tournamentUser in tournament.Users)
{
if (_users.All(i => i.Id != tournamentUser.Id))
{
_tournament.Users.Add(tournamentUser);
}
}
// Removes old Users
foreach (var tournamentUser in _users)
{
if (tournament.Users.FirstOrDefault(tu => tu.Id == tournamentUser.Id) == null)
{
_tournament.Users.Remove(tournamentUser);
}
}
await _context.SaveChangesAsync();
return CreatedAtAction(nameof(GetTournament), new { id = tournament.Id }, tournament);
}