C# 如何在没有跟踪问题(已被跟踪)的情况下插入多对多实体?
我有以下型号/表格:C# 如何在没有跟踪问题(已被跟踪)的情况下插入多对多实体?,c#,.net-core,entity-framework-core,C#,.net Core,Entity Framework Core,我有以下型号/表格: CV: int ID string Title List<SkillCV> SkillsCVs // many-to-many junction/associative model/table to link the 2 tables SkillCV: int CvID int SkillID bool IsCore Skill: int ID string Title CV: 整数ID 字符串标题 列出SK
CV:
int ID
string Title
List<SkillCV> SkillsCVs // many-to-many junction/associative model/table to link the 2 tables
SkillCV:
int CvID
int SkillID
bool IsCore
Skill:
int ID
string Title
CV:
整数ID
字符串标题
列出SKILLSCV//多对多连接/关联模型/表格以链接这两个表格
技能简历:
int CvID
英特斯基利德酒店
布尔伊斯科尔酒店
技能:
整数ID
字符串标题
然后我有以下方法:
public async Task<int> UpdateAsync(CV cv, List<SkillsCVs> skillsCVs)
{
var cvDB = await context.Set<CV>()
.Include(CV => CV.SkillsCVs)
.ThenInclude(skillCV => skillCV.Skill)
.Include(CV => CV.User)
.FirstOrDefaultAsync(CV => CV.Id == cv.Id);
cvDB.Title = cv.Title;
cvDB.SkillsCVs.Clear();
cvDB.SkillsCVs.AddRange(skillsCVs);
context.Set<CV>().Update(cvDB); // EXCEPTION
return await context.SaveChangesAsync();
}
public异步任务UpdateAsync(CV-CV,List-skillsCVs)
{
var cvDB=await context.Set()
.包括(CV=>CV.SkillsCV)
.然后包括(skillCV=>skillCV.Skill)
.Include(CV=>CV.User)
.FirstOrDefaultAsync(CV=>CV.Id==CV.Id);
cvDB.Title=cv.Title;
cvDB.SkillsCVs.Clear();
cvDB.SkillsCVs.AddRange(SkillsCVs);
context.Set().Update(cvDB);//异常
返回wait context.SaveChangesAsync();
}
问题是我有一个CVSkill实体,CV id为8,Skill id为1,所以复合键是(1,8)。然后我用这个方法将(1,14)-ID为1的相同技能添加到ID为14的CV中,它表示ID为8的CV已经被跟踪,或者(1,8)无法复制。我想技能1仍然可以追溯到CV 8,因为我加入了CV->SkillCV->Skill。我如何更新ID为14的CV以将(1,14)插入其SkillsCVs集合中,而不存在跟踪/复制问题?我认为同时拥有(1,8)和(1,14)实体不是问题,因为组合键作为一个整体是唯一的,即使技能ID是相同的
编辑: 现在我有以下代码:
public async Task<int> UpdateAsync(CV cv, List<SkillsCVs> skillsCVs)
{
var cvDB = await context.Set<CV>()
.AsNoTracking()
.Include(CV => CV.SkillsCVs)
.ThenInclude(skillCV => skillCV.Skill)
.Include(CV => CV.User)
.FirstOrDefaultAsync(CV => CV.Id == cv.Id);
cvDB.Title = cv.Title;
foreach (var skillCV in cvDB.SkillsCVs)
context.Entry(skillCV).State = EntityState.Detached;
cvDB.SkillsCVs.Clear();
foreach (var skillCV in skillsCVs)
cvDB.SkillsCVs.Add(skillCV);
context.Set<CV>().Update(cvDB); // EXCEPTION
return await context.SaveChangesAsync();
}
public异步任务UpdateAsync(CV-CV,List-skillsCVs)
{
var cvDB=await context.Set()
.AsNoTracking()
.包括(CV=>CV.SkillsCV)
.然后包括(skillCV=>skillCV.Skill)
.Include(CV=>CV.User)
.FirstOrDefaultAsync(CV=>CV.Id==CV.Id);
cvDB.Title=cv.Title;
foreach(cvDB.SkillsCVs中的var skillCV)
context.Entry(skillCV.State=EntityState.Distached;
cvDB.SkillsCVs.Clear();
foreach(SkillSCV中的var skillCV)
cvDB.SkillsCVs.Add(skillCV);
context.Set().Update(cvDB);//异常
返回wait context.SaveChangesAsync();
}
当我尝试添加单个实体skillCV(包含1个实体的skillsCVs集合)时,它抛出:Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException:数据库操作预期影响1行,但实际影响0行。自加载实体后,数据可能已被修改或删除。
当我试图通过ID为14的CV中的SkillsCV添加一组实体时,它抛出:System.InvalidOperationException:无法跟踪实体类型“CV”的实例,因为已跟踪另一个键值为“{ID:8}”的实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。
尝试使用likewait context.Set().AsNoTracking()
,它不应引发异常,因为它不再是跟踪实体
public async Task<int> UpdateAsync(CV cv, List<SkillsCVs> skillsCVs)
{
var cvDB = await context.Set<CV>()
.AsNoTracking()
.Include(CV => CV.SkillsCVs)
.ThenInclude(skillCV => skillCV.Skill)
.Include(CV => CV.User)
.FirstOrDefaultAsync(CV => CV.Id == cv.Id);
cvDB.Title = cv.Title;
cvDB.SkillsCVs.Clear();
cvDB.SkillsCVs.AddRange(skillsCVs);
context.Set<CV>().Update(cvDB); // EXCEPTION
return await context.SaveChangesAsync();
}
编辑2未在此处使用
AsNoTracking()
。更新代码以获取现有的SkillsCV,然后清除cvDB.SkillsCV
,并在添加新的SkillsCV时检查该SkillsCV是否已经存在。如果存在,则将现有的标记为已分离,将新添加的标记为已修改。还有注释行context.Set().Update(cvDB)代码>
public异步任务UpdateAsync(CV-CV,List-skillsCVs)
{
var cvDB=await context.Set()
.包括(CV=>CV.SkillsCV)
.然后包括(skillCV=>skillCV.Skill)
.Include(CV=>CV.User)
.FirstOrDefaultAsync(CV=>CV.Id==CV.Id);
cvDB.Title=cv.Title;
//检索现有SkillSCV的列表,以便稍后我们可以将其标记为已分离,将新添加的skillCV标记为已修改。
var existingSkillsCVs=cvDB.SkillsCVs.ToList();
cvDB.SkillsCVs.Clear();
foreach(skillsCV中的var skillsCV)
{
cvDB.SkillsCVs.Add(skillsCV);
//如果已经存在,则查找SkillsCV。如果存在,则将其分离并将新添加的SkillsCV设置为已修改。
var existingSkillsCV=existingSkillsCVs.FirstOrDefault(x=>x.CvID==skillsCV.CvID&&x.SkillID==skillsCV.SkillID);
if(existingSkillsCV!=null)
{
context.Entry(existingSkillsCV.State=EntityState.Distached;
context.Entry(existingSkillsCV.Skill).State=EntityState.Detached;
context.Entry(skillsCV).State=EntityState.Modified;
}
}
//context.Set().Update(cvDB);//注释此行
返回wait context.SaveChangesAsync();
}
尝试使用分离
来分离当前CVP
比如:
var-cvDB=await-context.Set()
.AsNoTracking()
.包括(CV=>CV.SkillsCV)
.然后包括(skillCV=>skillCV.Skill)
.Include(CV=>CV.User)
.FirstOrDefaultAsync(CV=>CV.Id==CV.Id);
cvDB.Title=cv.Title;
foreach(cvDB.skillsCV中的var skillsCV)
{
context.Entry(skillsCV.State=EntityState.Distached;
}
cvDB.SkillsCVs.Clear();
cvDB.SkillsCVs.AddRange(SkillsCVs);
context.Set().Update(cvDB);//例外情况
返回wait context.SaveChangesAsync();
它实际上生成了正确的SQL查询(我正在记录EF查询以查看SQL)。但是<代码>Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException:数据库操作预期影响1行,但实际影响0行。自从加载实体后,数据可能已被修改或删除。
当我删除.AsNoTracking()和更新时,它显示:Microsoft.Data.SqlClient.SqlException(0x80131904):违反主键约束“PK_SkillsCVs”。无法在对象“dbo.SkillsCVs”中插入重复键。重复的键值为(1,8)。
当它尝试插入键值(1,14)时,
public async Task<int> UpdateAsync(CV cv, List<SkillsCVs> skillsCVs)
{
var cvDB = await context.Set<CV>()
.Include(CV => CV.SkillsCVs)
.ThenInclude(skillCV => skillCV.Skill)
.Include(CV => CV.User)
.FirstOrDefaultAsync(CV => CV.Id == cv.Id);
cvDB.Title = cv.Title;
cvDB.SkillsCVs.Clear();
cvDB.SkillsCVs.AddRange(skillsCVs);
// context.Set<CV>().Update(cvDB); // comment this line
return await context.SaveChangesAsync();
}
public async Task<int> UpdateAsync(CV cv, List<SkillsCVs> skillsCVs)
{
var cvDB = await context.Set<CV>()
.Include(CV => CV.SkillsCVs)
.ThenInclude(skillCV => skillCV.Skill)
.Include(CV => CV.User)
.FirstOrDefaultAsync(CV => CV.Id == cv.Id);
cvDB.Title = cv.Title;
// Retrieve list of existingSkillsCVs so later we can mark it as Detached and newly added skillCV as Modified.
var existingSkillsCVs = cvDB.SkillsCVs.ToList();
cvDB.SkillsCVs.Clear();
foreach(var skillsCV in skillsCVs)
{
cvDB.SkillsCVs.Add(skillsCV);
// Find SkillsCV if already exists. If exists then Detach it & set newly added skillsCV as Modified.
var existingSkillsCV = existingSkillsCVs.FirstOrDefault(x => x.CvID == skillsCV.CvID && x.SkillID == skillsCV.SkillID);
if (existingSkillsCV != null)
{
context.Entry(existingSkillsCV).State = EntityState.Detached;
context.Entry(existingSkillsCV.Skill).State = EntityState.Detached;
context.Entry(skillsCV).State = EntityState.Modified;
}
}
// context.Set<CV>().Update(cvDB); // comment this line
return await context.SaveChangesAsync();
}
var cvDB = await context.Set<CV>()
.AsNoTracking()
.Include(CV => CV.SkillsCVs)
.ThenInclude(skillCV => skillCV.Skill)
.Include(CV => CV.User)
.FirstOrDefaultAsync(CV => CV.Id == cv.Id);
cvDB.Title = cv.Title;
foreach( var skillsCV in cvDB.SkillsCVs)
{
context.Entry(skillsCV).State = EntityState.Detached;
}
cvDB.SkillsCVs.Clear();
cvDB.SkillsCVs.AddRange(skillsCVs);
context.Set<CV>().Update(cvDB); // EXCEPTION
return await context.SaveChangesAsync();