Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/313.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 违反主键约束“PK_XY”。无法在对象“dbo.XY”中插入重复键_C#_.net_Entity Framework Core_Ef Core 5.0 - Fatal编程技术网

C# 违反主键约束“PK_XY”。无法在对象“dbo.XY”中插入重复键

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

使用EF Core,我有一个可以有多个土壤的区域,同一个土壤可以附着到多个区域:

存储库代码

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; }