C# 违反主键约束“PK_XY”。无法在对象“dbo.XY”中插入重复键
使用EF Core,我有一个可以有多个土壤的区域,同一个土壤可以附着到多个区域: 存储库代码C# 违反主键约束“PK_XY”。无法在对象“dbo.XY”中插入重复键,c#,.net,entity-framework-core,ef-core-5.0,C#,.net,Entity Framework Core,Ef Core 5.0,使用EF Core,我有一个可以有多个土壤的区域,同一个土壤可以附着到多个区域: 存储库代码 public Task UpdateAsync<T>(T entity) where T : BaseEntity { _dbContext.Entry(entity).State = EntityState.Modified; return _dbContext.SaveChangesAsync(); } 我用AutoMapper protected TBussinesM
public Task UpdateAsync<T>(T entity) where T : BaseEntity
{
_dbContext.Entry(entity).State = EntityState.Modified;
return _dbContext.SaveChangesAsync();
}
我用AutoMapper
protected TBussinesModel FromDto(TDto dto)
{
return _mapper.Map<TBussinesModel>(dto);
}
映射是这样的
[HttpPost]
[ValidateAntiForgeryToken]
public virtual async Task<IActionResult> Edit(TDto dto)
{
try
{
var entity = FromDto(dto);
BeforeUpdate(entity, dto);
await _repository.UpdateAsync(entity);
return RedirectToAction(nameof(Index));
}
catch (Exception ex)
{
_logger.LogError(ex, "when editing an object after submit");
return PartialView();
}
}
CreateMap<Zone, ZoneDTO>()
.ForMember(p => p.SolIds, o => o.MapFrom(p => p.Sols.Select(s => s.Id).ToArray()))
.ForMember(p => p.SolNoms, o => o.MapFrom(p => p.Sols.Select(s => s.Nom).ToArray()))
.ReverseMap();
由于SOL和Zone之间的关系是多对多关系,因此将创建一个包含两个表的主键的单独表。 这种多对多关系需要表定义。还可以尝试包含usingenety来指定用于定义关系的实体
modelBuilder
.Entity<Post>()
.HasMany(p => p.SOL)
.WithMany(p => p.ZONE)
.UsingEntity(j => j.ToTable("SOL_ZONE"));
当您从dto映射到实体时,FromDto方法将为您提供一个区域实体,该区域实体的SOL列表未填充该区域的现有SOL。这是一张空名单。那么,当你打电话的时候-
zone.Sols.Clear();
它什么也不做,而且在数据库级别,该区域仍然保留其SOL。然后,当您重新填充Sol列表时,您正试图将一些以前存在的Sol插入到列表中
在清除和重新填充之前,必须从数据库中获取现有区域及其Sols列表。如何做到这一点取决于存储库的实现方式
关于如何更新EF 5.0中的多对多实体,您可以查看
编辑:
尝试将基本控制器的编辑方法修改为-
[HttpPost]
[ValidateAntiForgeryToken]
public virtual async Task<IActionResult> Edit(TDto dto)
{
try
{
var zone = _repository.SingleOrDefault(new GetZoneWithPaysAndSolsSpecification(dto.Id));
zone.Sols.Clear();
foreach (var id in dto.SolIds)
{
var sol = _repository.GetById<Sol>(solId);
zone.Sols.Add(sol);
}
await _repository.UpdateAsync(zone);
return RedirectToAction(nameof(Index));
}
catch (Exception ex)
{
_logger.LogError(ex, "when editing an object after submit");
return PartialView();
}
}
这根本没有效率。试试像这样的东西-
var sols = // fetch all the sols from the database
foreach (var id in dto.SolIds)
{
zone.Sols.Add(sols.FirstOrDefault(p => p.Id == id));
}
在我看来,你有很多对很多的关系。因此,您需要设置一个具有主键的新实体,以便组合映射,并以正确的方式配置实体: 在3.x之前(含3.x)的EF Core中,需要在模型中包含一个实体来表示联接表,然后将导航属性添加到指向联接实体的多对多关系的任一侧: 联接表的主键是包含两个外键值的复合键。此外,多对多关系的双方都使用HasOne、WithMany和HasForeignKey Fluent API方法进行配置 如果希望通过Sol或分区实体访问Sol分区数据,这就足够了。如果要直接查询SolZone数据,还应为其添加一个数据库集:
public DbSet<SolZone> SolZones { get; set; }
您可以在这里的EF Core中查找不同的关系:从错误消息中,您将重复的密钥插入数据库。@Karney。我明白,谢谢。我在OP中添加了更多细节,以便更好地澄清问题context@Serge你能分享FromDto方法的代码吗?@atiyar我添加了代码,不确定它是否与问题相关,无论如何,如果有帮助,greatI没有指定表,所以它自动为我创建了表SolZone。。。我需要手动编写所有的datatables结构,每个链接的表,这会在业务代码中造成混乱。。。它实际上是数据库对象,不再是业务。顺便说一句,我使用最新的EF 5,可能管理方式不同?EF Core 5.0支持多对多关系,而不显式映射联接表。这里有一个链接,它是以不同的方式管理的:好的,那么您的解决方案对于早期版本的ef应该是可以的。。。EF 5自动为我创建了表yyes,您也不需要在OnModelCreating中添加配置。@Serge请检查编辑部分。如果这样做,我将丢失从Request@Serge首先尝试一下,看看你的问题是否得到解决。如果是,那么您可以从那里更新代码。我尝试过,但现在它显示:System.InvalidOperationException:无法跟踪实体类型“Zone”的实例,因为已跟踪键值为“{Id:1}”的另一个实例。附着现有实体时,请确保仅附着一个具有给定键值的实体实例。
zone.Sols.Clear();
[HttpPost]
[ValidateAntiForgeryToken]
public virtual async Task<IActionResult> Edit(TDto dto)
{
try
{
var zone = _repository.SingleOrDefault(new GetZoneWithPaysAndSolsSpecification(dto.Id));
zone.Sols.Clear();
foreach (var id in dto.SolIds)
{
var sol = _repository.GetById<Sol>(solId);
zone.Sols.Add(sol);
}
await _repository.UpdateAsync(zone);
return RedirectToAction(nameof(Index));
}
catch (Exception ex)
{
_logger.LogError(ex, "when editing an object after submit");
return PartialView();
}
}
foreach (var id in dto.SolIds)
{
var sol = _repository.GetById<Sol>(solId);
zone.Sols.Add(sol);
}
var sols = // fetch all the sols from the database
foreach (var id in dto.SolIds)
{
zone.Sols.Add(sols.FirstOrDefault(p => p.Id == id));
}
public class Sol
{
// ...
public ICollection<SolZone> SolZones { get; set; } = new List<Zone>();
}
public class Zone
{
// ...
public ICollection<SolZone> SolZones { get; set; } = new List<Sol>();
}
public class SolZone
{
public int SolId { get; set; }
public Sol Sol { get; set; }
public int ZoneId { get; set; }
public Zone Zone { get; set; }
}
// And in the OnModelCreating
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<SolZone>().HasKey(sc => new {sc.SolId , sc.ZoneId});
modelBuilder.Entity<SolZone>()
.HasOne<Sol>(s => s.Sol)
.WithMany(sz => sz.SolZones)
.HasForeignKey(s => s.SolId)`
modelBuilder.Entity<SolZone>()
.HasOne<Zone>(z => z.Zone)
.WithMany(sz => sz.SolZones)
.HasForeignKey(z => z.ZoneId);
}
public DbSet<SolZone> SolZones { get; set; }