Маппинг вложенных сущностей из DTO в Entity c EF Core

Рейтинг: 0Ответов: 0Опубликовано: 05.04.2023

Я хочу получить из 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();

Ответы

Ответов пока нет.