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}”的实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。

尝试使用like
wait 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();