EF Core Many to Many, использующий Automapper, возвращает цикл для ресурса API

Я использую .net Core с EF2, и я хочу настроить это отношение «Многие ко многим». У любого доктора может быть много назначений и наоборот. Также я хочу реализовать отображение Automapper сверху.

Структура модели высокого уровня

**Doctor**
 - Id
 - Name
 - Appointments
**Appointment**
 - Id
 - Name
 - Category
 - Doctors

Врачи Модель

[Table("Doctors")]
public class Doctor
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<DoctorAppointment> Appointments { get; set; }
    public Doctor()
    {
        Appointments = new Collection<DoctorAppointment>();
    }
}

Модель встреч

[Table("Appointments")]
public class Appointment
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Category { get; set; }
    public ICollection<DoctorAppointment> Doctors { get; set; }
    public Appointment()
    {
        Doctors = new Collection<DoctorAppointment>();
    }
}

Таблица для записи на приемы от многих ко многим врачам

[Table("DoctorAppointments")]
public class DoctorAppointment
{
    public int DoctorId { get; set; }
    public int AppointmentId { get; set; }
    public Doctor Doctor { get; set; }
    public Appointment Appointment { get; set; }  
}

Контекст БД

public class MyDocDbContext : DbContext
{
    public DbSet<Appointment> Appointments { get; set; }
    public DbSet<Doctor> Doctors { get; set; }

    public MyDocDbContext(DbContextOptions<MyDocDbContext> options) 
      : base(options)
    {
    }
    protected override void OnModelCreating(ModelBuilder modelBuilder) 
    {
        modelBuilder.Entity<DoctorAppointment>().HasKey(da => 
          new { da.DoctorId, da.AppointmentId });

        modelBuilder.Entity<DoctorAppointment>()
            .HasOne(da => da.Doctor)
            .WithMany(p => p.Appointments)
            .HasForeignKey(pt => pt.DoctorId);

        modelBuilder.Entity<DoctorAppointment>()
            .HasOne(pt => pt.Appointment)
            .WithMany(t => t.Doctors)
            .HasForeignKey(pt => pt.AppointmentId);
    }
}

Врачи и запись на прием Контроллеры:

    [HttpGet("appointment/{id}")]
    public async Task<IActionResult> GetAppointment(int id)
    {
        var app =  await context.Appointments
          .Include(a => a.Doctors)
          .ThenInclude(da => da.Doctor)        
          .SingleOrDefaultAsync(a => a.Id == id);
        return Ok(app);
    }

    [HttpGet("doctor/{id}")]
    public async Task<IActionResult> GetDoctor(int id)
    {
      var doc =  await context.Doctors   
        .Include(d => d.Appointments)
        .ThenInclude(da => da.Appointment)
        .SingleOrDefaultAsync(v => v.Id == id);
        return Ok(doc);
    }

Теперь реализуем Отображение ресурсов Automapper ...

До этого момента все работает нормально, так как при извлечении из этих двух сущностей он возвращает все отношения. Проблема возникает при использовании Automapper для сопоставления домена с ресурсом API. Использование следующих картографических ресурсов:

DoctorResource

public class DoctorResource
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<AppointmentResource> Appointments { get; set; }
    public DoctorResource()
    {
        Appointments = new Collection<AppointmentResource>();
    }
}

НазначениеРесурс

public class AppointmentResource
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Category { get; set; }
    public ICollection<DoctorResource> Doctors { get; set; }
    public AppointmentResource()
    {
        Doctors = new Collection<DoctorResource>();
    }

}

Наконец, отображение выглядит следующим образом:

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        // Domain to API Resource

        CreateMap<Doctor, DoctorResource>()
             .ForMember(dr => dr.Appointments, opt => opt
                 .MapFrom(d => d.Appointments
                 .Select(y => y.Appointment)
                 .ToList()));

        CreateMap<Appointment, AppointmentResource>()
            .ForMember(dr => dr.Doctors, opt => opt
                .MapFrom(d => d.Doctors
                .Select(y => y.Doctor)
                .ToList()));
    }
}

Итак, когда НЕТ использует ресурсы сопоставления, результат JSON будет следующим (желаемый):

**http://localhost:5000/api/appointment/1**

{
"id": 1,
"name": "Personal Trainer",
"category": "Fitness",
"doctors": [
    {
        "doctorId": 2027,
        "appointmentId": 1,
        "doctor": {
            "id": 2027,
            "name": "Dr. Cunha",
            "appointments": [],
        }
    },
    {
        "doctorId": 2028,
        "appointmentId": 1,
        "doctor": {
            "id": 2028,
            "name": "Dr. Gouveia",
            "appointments": [],
        }
    },
    {
        "doctorId": 2029,
        "appointmentId": 1,
        "doctor": {
            "id": 2029,
            "name": "Dr. Tiago",
            "appointments": [],
        }
    }
  ]
}

Однако при использовании Automapper для сопоставления ответа API расширяет категорию «Назначения» и «Врачи», и так далее ...

**http://localhost:5000/api/appointment/1**

{
"id": 1,
"name": "Personal Trainer",
"category": "Fitness",
"doctors": [
    {
        "id": 2027,
        "name": "Dr. Cunha",
        "appointments": [
            {
                "id": 1,
                "name": "Personal Trainer",
                "category": "Fitness",
                "doctors": [
                    {
                        "id": 2028,
                        "name": "Dr. Gouveia",
                        "appointments": [
                            {
                                "id": 1,
                                "name": "Personal Trainer",
                                "category": "Fitness",
                                "doctors": [
                                     { ....
                                      and so on...

Однако по какой-то неизвестной мне причине Доктор Контроллер возвращает желаемый результат.

Извините за длинный пост, но я не смог предоставить вам четкую картину, не раскрывая все это.

Ваше здоровье

Примените метод MaxDepth при создании карт. Итак, в вашем примере это будет CreateMap<Doctor, DoctorResource>() .ForMember(dr => dr.Appointments, opt => opt .MapFrom(d => d.Appointments .Select(y => y.Appointment) .ToList())).MaxDepth(1); и CreateMap<Appointment, AppointmentResource>() .ForMember(dr => dr.Doctors, opt => opt .MapFrom(d => d.Doctors .Select(y => y.Doctor) .ToList())).MaxDepth(1)

user1672994 01.05.2018 14:49

Боковое примечание: вам не нужны эти атрибуты ToTable. Множественное число по умолчанию.

Steve Greene 01.05.2018 16:04

Спасибо, MaxDepth решил проблему, и спасибо за подсказку, Стив.

Tiago Cunha 03.06.2018 20:39
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
894
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я предполагаю, что ваш MappingProfile должен быть изменен, как показано ниже:

public MappingProfile()
{
    // Domain to API Resource

    CreateMap<Doctor, DoctorResource>()
         .ForMember(dr => dr.Appointments, opt => opt
             .MapFrom(d => d.DoctorAppointment
             .Select(y => y.Appointment)
             .ToList()));

    CreateMap<Appointment, AppointmentResource>()
        .ForMember(dr => dr.Doctors, opt => opt
            .MapFrom(d => d.DoctorAppointment
            .Select(y => y.Doctor)
            .ToList()));
}

Вместо MapFrom (d => d.Doctors), а также MapFrom (d => d.Appointments) замените их на MapFrom (d => d.DoctorAppointment)

это должно быть работать.

Другие вопросы по теме