I have a problem with EF Core in combination with automapper.
My Solution contains this hierarchy:
BLL <=> DAL <=> EF Core <=> Mysql Database
I use automapper in the BLL.
And have as example this user 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; }
}
And i have the same class for the UserDTO (DataTransferObject).
I already have a record for an user in the database to create another user, because it's necessary to fill createdby and modifiedby to create or update a user.
Get the user from the database:
UserDTO userDTO = this._UserBLL.GetUser("Unit", "Test");
//BLL
var mapped = new List<UserDTO>();
this._Mapper.Map(dalResult, mapped); //dalResult = List<User> object
return mapped;
And here an example how to add a user:
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 Exception that is thrown: The instance of entity type 'User' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked.
But i dont know how to solve the exception. Same issue is, when i update an existing record.
AutoMapping class is:
this.CreateMap<User, UserDTO>()
.ReverseMap();
Do u have an idea how to fix this? I hope you understand my problem, else not, ask me.
King regards
There are a couple of misconceptions here
Bare in mind that a DTO is a dumb class, in a sense that it does nothing but serving as a container for the information to travel in a strongly typed way. You will be normally dealing with DTOs when the front-end send you some data and your backend catches on the controller parameter (I know this is not exactly true, just trying to make a point here), or when returning a DTO to the front end to whatever reason it needs. The general approach then will be:
Frontend sent a DTO
Backend controller catches
Backend services map the DTO to some domain model of interest (automapper)
Backend services perform the logical steps of the business rules such as saving a record on the database or updating something I don't know you name it
Backend service map the response (if any) to a DTO (automapper)
Backend controller sends the data right back to the front end, to do whatever it needs to do
Now, regarding your bug it's a common bug of entity frameworks begginers. Let me ilustrate what happend:
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.
Somewhere over the mapping you are creating the same object type with the same Id and then entity framework go bananas
//EF says: "mmm, that's weird, im tracking the same object
//again, im gonna alert the human. *throws exception*
The key here is to re-read the documentation of entity framework/core to fully understand how entity framework think Now, regarding your code and business requirement, you should be aiming to do something like this:
//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;
final words on this topic:
var user = Mapper.Map<User>(newUserDto); //hey mapper, be a pal and transform this newUserDto into a User object.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.