附加类型为“”的实体失败,因为另一个实体具有相同的主键值

Posted

技术标签:

【中文标题】附加类型为“”的实体失败,因为另一个实体具有相同的主键值【英文标题】:Attaching an entity of type '' failed because another entity has the same primary key value 【发布时间】:2017-09-25 15:25:39 【问题描述】:

我的 WebApi 应用程序有一个小问题。 当我将新实体添加到数据库中时会出现此问题。

实体:

public sealed class Lot : Entity

    [Required, DefaultValue(false)]
    public bool IsActive  get; set; 

    [Required, DefaultValue(false)]
    public bool IsDelete  get; set; 

    [Required, DefaultValue(false)]
    public bool InCredit  get; set; 

    [DefaultValue(1), Range(1, 10)]
    public int PhaseNumber  get; set; 

    [Required]
    public decimal DesiredPrice  get; set; 

    public Guid? AcceptedBetId  get; set; 

    [Required]
    public DateTime CreationDate  get; set; 

    public DateTime ExpirationDateTime  get; set; 

    [Required]
    public string City  get; set; 

    public User User  get; set; 

    public Car Car  get; set; 

    public ICollection<Bet> Bets  get; set; 

    public Lot()
    
        Bets = new List<Bet>();
    


public sealed class Bet : Entity

    [Required, Range(0.0, double.MaxValue)]
    public decimal Price  get; set; 

    [Required]
    public DateTime CreationDate  get; set; 

    [Range(0, int.MaxValue)]
    public int BetNumber  get; set; 

    public Lot Lot  get; set; 

    public User User  get; set; 

我想在我的数据库中添加新的 Bet 实体的代码:

Lot lot;
using (LotsManager lotsManager = new LotsManager())

     lot = await lotsManager.GetLotAsync(model.LotId);


User user;
using (UserManager userManager = new UserManager())

      user = await userManager.GetUserAsync(model.UserInfoId);


var bet = new Bet

      Id = Guid.NewGuid(),
      Price = model.Price,
      CreationDate = DateTime.Now,
      BetNumber = lastBetNumber + 1,
      Lot = lot,
      User = user
 ;

 await _betsManager.SaveBetAsync(bet);

在这个地方将我的实体添加到数据库中(数据库:我的 SQL)

protected async Task Save<T>(T entity) where T : Entity
    
        using (var dbContextTransaction = _context.Database.BeginTransaction())
        
            try
            
                var dbEntity = await _context.Set<T>().SingleOrDefaultAsync(x => x.Id == entity.Id);

                _context.Set<T>().Attach(entity);
                _context.Entry(entity).State = dbEntity != null ? EntityState.Modified : EntityState.Added;

                await _context.SaveChangesAsync();
                dbContextTransaction?.Commit();
            
            catch (DbEntityValidationException ex)
            
                dbContextTransaction?.Rollback();

                var error = ex.EntityValidationErrors.First().ValidationErrors.First();
                throw new InvalidModelException(error.ErrorMessage);
            
            catch (Exception ex)
            
                dbContextTransaction?.Rollback();

                throw new InvalidDbOperationException(ex.Message);
            
        
    

当我调用我的保存方法时,我的代码抛出了这个异常:

附加类型为“WebCar.Domain.Entities.Lot”的实体失败,因为同一类型的另一个实体已经具有相同的主键值。如果图中的任何实体具有冲突的键值,则在使用“附加”方法或将实体的状态设置为“未更改”或“已修改”时,可能会发生这种情况。这可能是因为某些实体是新实体,尚未收到数据库生成的键值。在这种情况下,使用“添加”方法或“已添加”实体状态来跟踪图形,然后将非新实体的状态设置为“未更改”或“已修改”。

我正在尝试使用

_context.Set<T>().Attach(entity);
_context.Entry(entity).State = EntityState.Unchanged;
or
_context.Entry(entity).State = EntityState.Unchanged;

但是代码还是会报错。

【问题讨论】:

【参考方案1】:

这是导致问题的代码:

  var dbEntity = await _context.Set<T>().SingleOrDefaultAsync(x => x.Id == entity.Id);
  _context.Set<T>().Attach(entity);

在这种情况下,如果您找到一个现有实体,它只是存储在上下文中。如果您尝试添加一个已经存在的,EF 将无法处理。有几种方法可以解决这个问题。我会展示两个:

 var dbEntity = await _context.Set<T>().AsNoTracking().SingleOrDefaultAsync(x => x.Id == entity.Id);

理论上,如果你不跟踪它,那么你应该可以添加另一个。

但是,我认为这种方式更好:

bool doesEntityExist = await _context.Set<T>().Any(x => x.Id == entity.Id);
_context.Set<T>().Attach(entity);
_context.Entry(entity).State = doesEntityExist ? EntityState.Modified : EntityState.Added;

【讨论】:

var dbEntity = await _context.Set&lt;T&gt;().AsNoTracking().SingleOrDefaultAsync(x =&gt; x.Id == entity.Id); _context.Set&lt;T&gt;().Attach(entity); context.Entry(entity).State = dbEntity != null ? EntityState.Modified : EntityState.Added; Thx,但问题仍然没有丢失:( 嗯,尝试删除 .Attach 调用。只需使用 Entry(entity).State 代替,然后。或者试试第二种风格。

以上是关于附加类型为“”的实体失败,因为另一个实体具有相同的主键值的主要内容,如果未能解决你的问题,请参考以下文章

附加类型实体失败,因为相同类型的另一个实体已经具有相同的主键值。

附加类型的实体失败,因为相同类型的其他实体已具有相同的主键值。在使用 "Attach" 方法或者将实体的状态设置为 "Unchanged" 或 "Mo

EF报错 附加类型model失败

使用实体框架编辑以前的数据

无法跟踪实体类型的实例,因为已在跟踪具有相同键值的另一个实例

无法跟踪实体类型的实例,因为已经在跟踪具有相同键值对的另一个实例 'Id'