带有 Automapper 的 EF Core 抛出异常 'Entity type cannot be tracked'

EF Core with Automapper throw Exception 'Entity type cannot be tracked'

我在将 EF Core 与自动映射器结合使用时遇到问题。

我的解决方案包含此层次结构:

BLL <=> DAL <=> EF Core <=> Mysql Database

我在 BLL 中使用了 automapper。

并以此用户为例 class:

public class User
{
    public Guid Id { get; set; } = Guid.NewGuid();

    public string FirstName { get; set; }

    public string LastName { get; set; }

    public Guid? CreatedById { get; set; }

    public Guid? ModifiedById { get; set; }

    public virtual User CreatedBy { get; set; }

    public virtual User ModifiedBy { get; set; }
}

我对 UserDTO (DataTransferObject) 有相同的 class。

我在数据库中已有一个用户创建另一个用户的记录,因为创建或更新用户需要填写createdby和modifiedby。

从数据库中获取用户:

UserDTO userDTO = this._UserBLL.GetUser("Unit", "Test");

//BLL
var mapped = new List<UserDTO>();
this._Mapper.Map(dalResult, mapped); //dalResult = List<User> object
return mapped;

这里是一个如何添加用户的例子:

UserDTO testUser2 = new UserDTO
{
    FirstName = "Test 2",
    LastName = "Test 2",
    CreatedBy = userDTO,
    ModifiedBy = userDTO
};

this._UserBLL.Add(testUser2);

//BLL
List<User> mappedUsers = new List<User>();

this._Mapper.Map(users, mappedUsers); //users = List<UserDTO> that contains testUser2

DbContext.Users.AddRange(mappedUsers); //Throws exception
DbContext.SaveChanges();

抛出的异常:The instance of entity type 'User' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked.

但是我不知道如何解决异常。同样的问题是,当我更新现有记录时。

AutoMapping class 是:

this.CreateMap<User, UserDTO>()
    .ReverseMap();

你知道如何解决这个问题吗? 我希望你能理解我的问题,否则,问我。

国王问好

这里有几个误解

  1. 请记住,DTO 是一个愚蠢的 class,从某种意义上说,它除了作为信息以强类型方式传输的容器之外什么都不做。 当 front-end 向您发送一些数据并且您的后端捕获到控制器参数时,您通常会处理 DTO(我知道这不完全正确,只是想在这里说明一点),或者将 DTO 返回到前端出于任何需要的原因。 那么一般的做法是:

  2. 前端发送了一个 DTO

  3. 后端控制器捕获

  4. 后端服务将 DTO 映射到某个感兴趣的领域模型(自动映射器)

  5. 后端服务执行业务规则的逻辑步骤,例如 在数据库中保存一条记录或更新一些我不知道你命名的东西

  6. 后端服务将响应(如果有)映射到 DTO(自动映射器)

  7. 后端controller直接把数据传回前端,做它该做的事

现在,关于您的错误,这是 entity framework 初学者的常见错误。让我来说明发生了什么:

UserDTO userDTO = this._UserBLL.GetUser("Unit", "Test"); 
//tip: as per the explanation above this should't be a DTO

//EF says "alright!, programmer pull out
//a record from the database and got a object of type UserDTO with Id = "someguid"
//im gonna begin tracking this object to see if it changes in the
//execution of the code.
//In case my fellow programmer wants to modify this object and 
//save it in on the database, as I was tracking it down in 
//the first place, I'm gonna know how to build my
//SQL statements to perform the right update/delete/create sentence in 
//SQL form.

您正在映射的某处创建具有相同 ID 的相同对象类型 然后entity framework发疯

//EF says: "mmm, that's weird, im tracking the same object 
//again, im gonna alert the human. *throws exception*

这里的关键是 re-read 实体 framework/core 的文档,以充分理解 entity framework 是如何思考的 现在,关于您的代码和业务需求,您的目标应该是做这样的事情:

//Suppose that the in the controller parameter we receive this DTO
var newUserDto = new UserDto() 
{
    Firstname = "Rodrigo",
    Lastname = "Ramirez",
    CreatedBy = 1, //this can be the user Id of the user that sent this DTO
};

var userToCreate = Mapper.Map<User>(newUserDto);
var userCreator = UserRepository.GetById(newUserDto.CreatedBy);
userToCreate.CreatedBy = userCreator;

关于这个话题的最后一句话:

  1. 这是一个偏好问题,但我更愿意阅读
var user = Mapper.Map<User>(newUserDto); //hey mapper, be a pal and transform this newUserDto into a User object.
  1. 检查您是否在不需要时使用 List<> 进行映射。
  2. 对于此业务逻辑,您无需阻止 entity framework 即可开始跟踪 给定的实体。但是,如果您发现这种情况,您可以检查执行此操作的方法“AsNoTracking()”。
  3. 查看在用户 class 实例化上创建新 GUID 的必要性。这似乎有问题,您需要充分了解 EF 的工作原理才能成功实施此实践。
  4. 您想做的事情是一种常见的审计做法,建议您覆盖方法 OnSaveChanges() 来执行此任务。