首页 > 解决方案 > EF Core 多对多使用 Automapper 在 API 资源上返回循环

问题描述

我将 .net Core 与 EF2 一起使用,我想设置这种多对多关系。任何医生都可以有很多约会,反之亦然。我还想在顶部实现 Automapper 映射。

高级模型结构

**医生**
 - ID
 - 姓名
 - 约会
**预约**
 - ID
 - 姓名
 - 类别
 - 医生

医生模型

[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 资源时会出现问题。使用以下映射资源:

博士资源

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 响应时,它会再次扩展 Appointments 类别和 Doctors ,依此类推......

**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...

然而,出于某种未知的原因,Doctor Controller会返回所需的输出。

很抱歉这篇冗长的帖子,但我无法在不暴露整个内容的情况下为您提供清晰的图片。

干杯

标签: c#entity-frameworkasp.net-coremany-to-manyautomapper

解决方案


我假设你的 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) 替换它们

它应该是工作。


推荐阅读