当我尝试使用带有AutoMapper的Entity Framework Core更新图时出现异常

时间:2018-08-22 18:48:34

标签: c# entity-framework-core automapper

尝试更新图形时出现异常。我使用Automapper将DTO映射到我的实体,然后在执行更新操作之前检查数据库中是否已存在我的对象。

当我致电dbContext.Update时,出现此异常

  

无法跟踪实体类型“ Order”的实例,因为已经跟踪了另一个具有相同“ {'OrderId'}”键值的实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。考虑使用'DbContextOptionsBuilder.EnableSensitiveDataLogging'查看冲突的键值。'

我的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AutoMapper;
using EFCoreIssue;
using EFCoreIssue.Dtos;
using EFCoreIssue.Models;
using Microsoft.EntityFrameworkCore;
using Xunit;

namespace EFCoreIssue_Tests
{
  public class OrdersTests
  {
    private readonly OrderRepository _orderRepository;

    public OrdersTests()
    {
        _orderRepository = new OrderRepository(new OrdersDbContext());
        Mapper.Initialize(cfg => cfg.CreateMap<OrderDto, Order>());
    }

    [Fact]
    public async void Should_Update_Customer_Successfully()
    {
        //Arrange
        var order = new Order
        {
            Description = "Test Order",
            OrderLines = new List<OrderLine>
            {
                new OrderLine {ProductId = 1, Quantity = 1}
            }
        };
        using (var dbContext = new OrdersDbContext())
        {
            dbContext.Orders.Add(order);
            await dbContext.SaveChangesAsync();
        }

        //Act
        var orderDto = new OrderDto
        {
            Description = "Test Order 2",
            OrderLines = new List<OrderLineDto>
            {
                new OrderLineDto {OrderLineId = order.OrderLines[0].OrderLineId, ProductId = 2, Quantity = 2}
            },
            OrderId = order.OrderId
        };

        await _orderRepository.UpdateAsync(Mapper.Map<Order>(orderDto));

        //Assert
        using (var dbContext = new OrdersDbContext())
        {
            var updatedOrder = await dbContext.Orders.Include(c => c.OrderLines)
                .Where(c => c.OrderId == order.OrderId).SingleAsync();
            //updatedOrder.ShouldNotBeNull();
            //customerFetch.Id.ShouldNotBeNull();
            //customerFetch.Id.ShouldBe(customerDto.Id);
        }
    }
  }
}

和我的订单存储库更新方法

    public async Task<Order> UpdateAsync(Order order)
    {
        var dbOrder = await GetOrderAsync(order.OrderId);
        if (dbOrder == null)
            throw new Exception($"Invalid Or Missing Order with Id {order.OrderId}");

        _dbContext.Update(order);
        await _dbContext.SaveChangesAsync();
        return order;
    }

注意:您可以在此github存储库上获取代码 code repository

2 个答案:

答案 0 :(得分:1)

在不使用AsNoTracking的情况下查询数据库某些记录时,EF Core将开始在当前上下文中对其进行跟踪。因此,当您在代码中更新实体时,EF Core会找到具有相同ID的多个实体,因此会出现错误。 AsNoTracking是一种解决方案,因为您不希望EF Core跟踪对此的任何修改。在许多情况下,只要您不在上下文中添加/附加/更新具有相同ID的实体,则不使用AsNoTracking就可以了。但最好在不需要跟踪时明确显示它。

答案 1 :(得分:0)

您的代码几乎没有问题。首先,您无法在需要时随时更新OrdersDbContext。如果这样做,则无法按照说明的异常消息来跟踪实体的实例。

对于单元测试,您需要使用内存数据库或带有模拟库(例如Moq)的模拟OrdersDbContext。

如果调用异步扩展方法,则需要实现async query provider