实体框架:更新具有 IEnumerable 属性的实体时出错。 'Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException'

Posted

技术标签:

【中文标题】实体框架:更新具有 IEnumerable 属性的实体时出错。 \'Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException\'【英文标题】:Entity Framework: Error updating an Entity with IEnumerable property. 'Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException'实体框架:更新具有 IEnumerable 属性的实体时出错。 'Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException' 【发布时间】:2021-01-09 08:59:29 【问题描述】:

我正在 ASP.NET Core 中开发我的第一个 webapi。我有两个实体:

public class Event : Entity

    private ISet<Ticket> _tickets = new HashSet<Ticket>();
    public string Name  get; protected set; 
    public string Description  get; protected set; 
    public DateTime StartDate  get; protected set; 
    public DateTime EndDate  get; protected set; 
    public DateTime CreatedAt  get; protected set; 
    public DateTime UpdatedAt  get; protected set; 
    public IEnumerable<Ticket> Tickets => _tickets;

    //.....

     public void AddTickets(int amount, decimal price)
    
        var seating = _tickets.Count + 1;
        for (var i = 0; i < amount; i++)
        
            _tickets.Add(new Ticket(this, seating, price));
            seating++;
        
    
 

  public class Ticket : Entity

    public Guid EventId  get; protected set; 
    public decimal Price  get; protected set; 
    //... and other properties

当我尝试创建新活动并为其分配门票时:

public class EventController : Controller

   [HttpPost]
    public async Task<IActionResult> AddEvent([FromBody] CreateEventCommand createEventCommand)
    
        createEventCommand.Id = Guid.NewGuid();
        await _eventService.AddAsync(createEventCommand.Id,
            createEventCommand.Name,
            createEventCommand.Description,
            createEventCommand.StartDate,
            createEventCommand.EndDate);

        await _eventService.AddTicketAsync(createEventCommand.Tickets,
            createEventCommand.Price,
            createEventCommand.Id);

        return Created($"/events/createEventCommand.Name", null);
        
    
 
 
 public class EventService : IEventService
 
            public async Task AddAsync(Guid id, string name, string description, DateTime startDate, DateTime endDate)
    
        var @event = await _eventRepository.GetAsync(id);
        if(@event != null)
        
            throw new Exception($"Event with this id: @event.Id already exist");
        

        var eventToAdd = new Event(id, name, description, startDate, endDate);
        await _eventRepository.AddAsync(eventToAdd);
    

    public async Task AddTicketAsync(int amount, decimal price, Guid eventId)
    
        var @event = await _eventRepository.GetAsync(eventId);
        if (@event == null)
        
            throw new Exception($"Event with this id: @event.Id already exist");
        

        @event.AddTickets(amount, price);
        await _eventRepository.UpdateAsync(@event);

    
  

 public class EventRepository : IEventRepository
 
    public async Task AddAsync(Event @event)
    
        _context.Events.Add(@event);
        await _context.SaveChangesAsync();
    
    
     public async Task UpdateAsync(Event @event)
    
        _context.Events.Update(@event);
        await _context.SaveChangesAsync();
    
   

在Tickets表上执行的查询是

  UPDATE [Tickets] SET [EventId] = @p700, [Price] = @p701, [PurchasedAt] = @p702, [Seating] = @p703, [UserId] = @p704, [Username] = @p705
  WHERE [Id] = @p706;
  SELECT @@ROWCOUNT;

之后会抛出异常:

Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded.

我的问题是:在这种情况下不应该是 INSERT 查询而不是 UPDATE 吗?在这种情况下并发问题的原因可能是什么?我是 .NET 的新手,一般来说是并发,如果有任何帮助,我将不胜感激。

【问题讨论】:

您可以将 Ticket.Id 属性配置为 ValueGeneratedNever [DatabaseGenerated(DatabaseGeneratedOption.None)] 您不是通过 context.Tickets.Add() 添加票证,而是将它们添加到 event.Tickets 集合中。所以它们没有设置为添加的实体状态。相反,它们在逻辑上未被跟踪。在这种情况下,更改跟踪器将未跟踪的实体视为已修改。 感谢您的评论。看起来你是对的。如果我在一个步骤中添加带有门票的事件(context.Events.AddAsync(//带有门票的事件)然后 SaveAsync()它工作正常。如果它按顺序发生:'添加事件>保存>更新事件实体使用票>保存'然后我得到一个错误。我该如何更改更改跟踪器的行为呢?通过添加:.UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll)) 到 AddDbContext 方法中的选项它不起作用。 我想我根据您的第一条评论找到了原因。我所有的实体都继承自带有构造函数的 Entity 基类:protected Entity() Id = Guid.NewGuid();当我删除 Id 生成时,它工作正常...... 我不确定这是否有意义,但看起来门票实体在生成时已经 Id != null 所以 EF 想要更新它们而不是插入新记录...跨度> 【参考方案1】:

在您的 AddTicketAsync 方法中,您检索 Event 对象的附加实体。

您应该能够通过调用 AddTickets 方法并在保存更改后对其进行更新。

 @event.AddTickets(amount, price);
 _eventRepository.your_context.SaveChangesAsync();

通过这种方式,您将保存更新后的事件实体的状态

您遇到并发错误,因为您正在尝试更新附加的实体。以下帖子可以帮助你How to update record using entity framework core?

【讨论】:

感谢您的回复。不幸的是它没有工作。仍然返回相同的错误... 你可以尝试分离你的 eventToAdd var entity = _context.Events.Add(@event); _context.detach(实体);等待 _context.SaveChangesAsync();

以上是关于实体框架:更新具有 IEnumerable 属性的实体时出错。 'Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException'的主要内容,如果未能解决你的问题,请参考以下文章

使用 AJAX 绑定时创建/更新后具有 IEnumerable 属性的 Kendo Grid 模型未正确更新

无法在实体框架中更新具有唯一约束索引的实体

如何在实体框架中查找具有指定日期范围列表的日期?

该属性必须是有效的实体类型,并且该属性应该具有非抽象的 getter 和 setter。实体框架 [重复]

实体框架代码首先用于具有属性集合的对象

如何首先使用实体​​框架 5 代码删除具有导航属性的对象?