Маппинг вложенных сущностей из DTO в Entity c EF Core
Я хочу получить из DTO сущность с помощью Automapper, для того чтобы добавить её в БД. Получаю следующую ошибку:
The instance of entity type 'LessonTemplate' cannot be tracked because another instance with the key value '{Id: 1}' is already being tracked.
When attaching existing entities, ensure that only one entity instance with a given key value is attached.
Причём ошибки со Student нет, хотя мапплю их, вроде как, одинаково.
Сущность
public class Lesson
{
[Key]
public int Id { get; set; }
public DayOfWeek Day { get; set; }
public TimeSpan Start { get; set; }
public TimeSpan End { get; set; }
public string Place { get; set; }
public Student? Student { get; set; }
public int? ChosenTemplateId { get; set; }
public List<LessonTemplate> AllowedTemplates { get; set; }
}
DTO
public class LessonDTO
{
public int Id { get; set; }
public DayOfWeek Day { get; set; }
public TimeOnly Start { get; set; }
public TimeOnly End { get; set; }
public string Place { get; set; }
public string? StudentLogin { get; set; }
public int? ChosenTemplateId { get; set; }
public List<int> AllowedTemplatesId { get; set; }
}
Конфиг Маппера
CreateMap<LessonDTO, Lesson>()
.ForMember(dest => dest.Start, opt => opt.ConvertUsing<TimeOnlyConverter, TimeOnly>())
.ForMember(dest => dest.End, opt => opt.ConvertUsing<TimeOnlyConverter, TimeOnly>())
.ForMember(dest => dest.Student,
opt => opt.ConvertUsing<StudentConverter, string>(src => src.StudentLogin))
.ForMember(dest => dest.AllowedTemplates,
opt => opt.ConvertUsing<LessonTemplateConverter, List<int>>(src => src.AllowedTemplatesId));
StudentConverter
public Student? Convert(string sourceMember, ResolutionContext context)
{
return sourceMember == null ? null
: studentRepository.GetByLogin(sourceMember);
}
public Student GetByLogin(string login)
{
return Set.FirstOrDefault(e => e.Login == login);
}
LessonTeplatesConverter
public List<LessonTemplate> Convert(List<int> sourceMember, ResolutionContext context)
{
if (sourceMember == null)
return null;
var result = new List<LessonTemplate>(sourceMember.Count);
foreach (var id in sourceMember)
{
var template = templatesRepository.GetById(id);
if (template != null)
result.Add(template);
}
return result;
}
public LessonTemplate? GetById(int id)
{
return Set
.Include(e => e.Tutor)
.FirstOrDefault(e => e.Id == id);
}
Set => dataContext.Set
Код сервиса
var lesson = mapper.Map<Lesson>(lessonDto);
await lessonsRepository.AddAsync(lesson);
await lessonsRepository.SaveChangesAsync();
return Result.Ok();
Под капотом репозиторий вызывает одноимённые методы EF.
Остальные сущности
public class Student
{
[Key]
public int Id { get; set; }
public string Login { get; set; }
public string PasswordHash { get; set; }
public string SecondName { get; set; }
public string FirstName { get; set; }
public string? ThirdName { get; set; }
public string PhoneNumber { get; set; }
public List<Comment> Comments { get; set; }
}
public class LessonTemplate
{
[Key]
public int Id { get; set; }
public Tutor Tutor { get; set; }
public string Name { get; set; }
public int Price { get; set; }
public string? Description { get; set; }
public List<Lesson> Lessons { get; set; }
}
public class Tutor
{
[Key]
public int Id { get; set; }
public string Login { get; set; }
public string PasswordHash { get; set; }
public string SecondName { get; set; }
public string FirstName { get; set; }
public string? ThirdName { get; set; }
public string PhoneNumber { get; set; }
public List<LessonTemplate> LessonTemplates { get; set; }
}
Если в сервисе самому доставать LessonTemplates, то всё работает нормально
var lesson = mapper.Map<Lesson>(lessonDto);
lesson.AllowedTemplates = database.LessonTemplates
.Where(e => lessonDto.AllowedTemplatesId.Contains(e.Id))
.ToList();
await lessonsRepository.AddAsync(lesson);
await lessonsRepository.SaveChangesAsync();
return Result.Ok();
Источник: Stack Overflow на русском