Я использую .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...
Однако по какой-то неизвестной мне причине Доктор Контроллер возвращает желаемый результат.
Извините за длинный пост, но я не смог предоставить вам четкую картину, не раскрывая все это.
Ваше здоровье
Боковое примечание: вам не нужны эти атрибуты ToTable. Множественное число по умолчанию.
Спасибо, MaxDepth решил проблему, и спасибо за подсказку, Стив.
Я предполагаю, что ваш 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)
это должно быть работать.
Примените метод
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)