Вывод в сущности вложенного списка в EF
Я новичок в WebApi и пытаюсь, используя Code First и Entity Framework, создать структуру базы по схеме. Одна из моих задач это создание модели и инициализирование бд тестовыми данными.
Вот моя первая модель.
using System.Collections.Generic;
// Объект потребления
namespace WebService.Models
{
public class ConsumptionObject : Object
{
public int SubOrganisationID { get; set; }
public SubOrganisation SubOrganisation { get; set; }
public ICollection<ElectricityMeteringPoint> ElectricityMeteringPoint { get; set; }
public ICollection<ElectricitySupplyPoint> ElectricitySupplyPoint { get; set; }
}
}
Вот вторая.
using System.Collections.Generic;
//Точка измерения электроенергии
namespace WebService.Models
{
public class ElectricityMeteringPoint : Point
{
public int ConsumptionObjectID { get; set; }
public ConsumptionObject ConsumptionObject { get; set; }
public int VoltageTransformerID { get; set; }
public VoltageTransformer VoltageTransformer { get; set;}
public int ElectricalTransformerID { get; set; }
public ElectricalTransformer ElectricalTransformer { get; set; }
public int ElectricityMeterID { get; set; }
public ElectricityMeter ElectricityMeter { get; set; }
public ICollection<MeteringDevice> MeteringDevice { get; set; }
}
}
Имея связь один-ко-многим мне нужно сделать АПИ по ConsumptionObject внутри которого будет вложенный список со всеми ElectricityMeteringPoint. Я перечитал уже кучу статей и много чего перепробовал но у меня не выходит, при запросе на ConsumptionObject вложенный ElectricityMeteringPoint всегда null, я не понимаю как его добавить.
Вот мой контекст, там я пытаюсь инициализировать тестовые данные и настроить вывод.
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
namespace WebService.Models
{
public class AppTestContext : DbContext
{
public AppTestContext(DbContextOptions<AppTestContext> options) : base(options)
{
Database.EnsureDeleted();
Database.EnsureCreated();
}
public DbSet<Organisation> Organisation { get; set; }
public DbSet<SubOrganisation> SubOrganisation { get; set; }
public DbSet<ConsumptionObject> ConsumptionObject { get; set; }
public DbSet<ElectricitySupplyPoint> ElectricitySupplyPoint { get; set; }
public DbSet<MeteringDevice> MeteringDevice { get; set; }
public DbSet<ElectricityMeteringPoint> ElectricityMeteringPoint { get; set; }
public DbSet<ElectricityMeter> ElectricityMeter { get; set; }
public DbSet<ElectricalTransformer> ElectricalTransformer { get; set; }
public DbSet<VoltageTransformer> VoltageTransformer { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ElectricityMeteringPoint>()
.HasOne(a => a.ElectricalTransformer).WithOne(b => b.ElectricityMeteringPoint)
.HasForeignKey<ElectricalTransformer>(e => e.ElectricityMeteringPointID);
modelBuilder.Entity<ElectricityMeteringPoint>()
.HasOne(a => a.ElectricityMeter).WithOne(b => b.ElectricityMeteringPoint)
.HasForeignKey<ElectricityMeter>(e => e.ElectricityMeteringPointID);
modelBuilder.Entity<ElectricityMeteringPoint>()
.HasOne(a => a.VoltageTransformer).WithOne(b => b.ElectricityMeteringPoint)
.HasForeignKey<VoltageTransformer>(e => e.ElectricityMeteringPointID);
/* Наполнение тестовыми данными */
//Счетчик электрической энергии
modelBuilder.Entity<ElectricityMeter>().HasData(
new ElectricityMeter[]
{
new ElectricityMeter {ID=1, No="123", Type="A1", VerificationDate=new DateTime(2020, 01, 13), ElectricityMeteringPointID=1},
new ElectricityMeter {ID=2, No="456", Type="B1", VerificationDate=new DateTime(2021, 02, 14), ElectricityMeteringPointID=2},
new ElectricityMeter {ID=3, No="789", Type="C1", VerificationDate=new DateTime(2022, 03, 15), ElectricityMeteringPointID=3},
});
//Точка измерения электроэнергии
modelBuilder.Entity<ElectricityMeteringPoint>(b =>{
b.HasData(new ElectricityMeteringPoint[]
{
new ElectricityMeteringPoint {ID=1, Name="Точка измерения 1", ConsumptionObjectID=1, ElectricityMeterID=1},
new ElectricityMeteringPoint {ID=2, Name="Точка измерения 2", ConsumptionObjectID=1, ElectricityMeterID=2},
new ElectricityMeteringPoint {ID=3, Name="Точка измерения 3", ConsumptionObjectID=1, ElectricityMeterID=3},
});
//b.HasOne(b => b.ConsumptionObject).WithMany(b =>b.ElectricityMeteringPoint).HasForeignKey(e => e.ConsumptionObjectID);
//b.OwnsOne(b => b.ConsumptionObject).WithOwner().HasForeignKey("ConsumptionObjectID");
});
//Объект потребления
modelBuilder.Entity<ConsumptionObject>(b =>
{
b.HasData(new ConsumptionObject[]
{
new ConsumptionObject {ID=1, Name="ПС 110/10 Весна", Address="Москва"}
});
//b.HasMany(g => g.ElectricityMeteringPoint).WithOne(e => e.ConsumptionObject).HasForeignKey(e => e.ConsumptionObjectID);
// b.OwnsMany<ElectricityMeteringPoint>(p => p.ElectricityMeteringPoint, a =>
// {
// a.WithOwner().HasForeignKey("ConsumptionObjectID");
// a.Property<int>("ID");
// a.Property<string>("Name");
// a.HasKey("ID", "Name");
// });
});
}
}
}
И собственно вот что я получаю когда делаю запрос на https://localhost:8050/api/ConsumptionObject/1 чтобы я не придумывал.
{
"subOrganisationID": 0,
"subOrganisation": null,
"electricityMeteringPoint": null,
"electricitySupplyPoint": null,
"id": 1,
"name": "ПС 110/10 Весна",
"address": "Москва"
}
Подскажите пожалуйста что я делаю не так и как мне получить вложенный список.
Ответы (2 шт):
Решить проблему помогла загрузка связанных данных. Для этого мне потребовалось немного видоизменить данный блок в контроллере добавив туда Include и FirstOrDefaultAsync.
// GET: api/ConsumptionObject/5
[HttpGet("{id}")]
public async Task<ActionResult<ConsumptionObject>> GetConsumptionObject(int id)
{
var consumptionObject = await _context.ConsumptionObject.Include(b => b.ElectricityMeteringPoint).FirstOrDefaultAsync(i => i.ID == id);
if (consumptionObject == null)
{
return NotFound();
}
return consumptionObject;
}
А также чтобы не было ошибок зацикливания когда одна модель ссылается на другую я установил Microsoft.AspNetCore.Mvc.NewtonsoftJson и заигнорил некоторые свойства вот таким способом.
using System.Collections.Generic;
using System.Text.Json.Serialization;
// Объект потребления
namespace WebService.Models
{
public class ConsumptionObject : Object
{
public int SubOrganisationID { get; set; }
[JsonIgnore]
public SubOrganisation SubOrganisation { get; set; }
public ICollection<ElectricityMeteringPoint> ElectricityMeteringPoint { get; set; } = new List<ElectricityMeteringPoint>();
public ICollection<ElectricitySupplyPoint> ElectricitySupplyPoint { get; set; } = new List<ElectricitySupplyPoint>();
}
}
using System.Collections.Generic;
using System.Text.Json.Serialization;
//Точка измерения электроенергии
namespace WebService.Models
{
public class ElectricityMeteringPoint : Point
{
public int ConsumptionObjectID { get; set; }
[JsonIgnore]
public ConsumptionObject ConsumptionObject { get; set; }
public int VoltageTransformerID { get; set; }
[JsonIgnore]
public VoltageTransformer VoltageTransformer { get; set;}
public int ElectricalTransformerID { get; set; }
[JsonIgnore]
public ElectricalTransformer ElectricalTransformer { get; set; }
public int ElectricityMeterID { get; set; }
[JsonIgnore]
public ElectricityMeter ElectricityMeter { get; set; }
public ICollection<MeteringDevice> MeteringDevice { get; set; }
}
}
Я не знаю насколько правильно у меня все получилось но данное решение мне помогло.
Если у вас есть лучшее решение я с удовольствием почитаю. Спасибо
Вы можете по другому спроектировать ваши сущности. Раздробив все на составляющие. Первая сущность ConsumptionObject, вторая сущность ElectricityMeteringPoint. Третью сущность (список) можно не плодить. В сущности ElectricityMeteringPoint, добавляете виртуальное свойство public virtual (типа) ConsumptionObject, с названием, например ConsumObject. Далее этот также добавляем ссылку на Id ConsumptionObject. Для этого нужно добавить свойство например int ConsumObjectId. Так что, в классе ElectricityMeteringPoint, должен быть такой код:
public int ConsumObjectId{get; set;};
[ForeignKey("ConsumObjectId")]
public virtual ConsumptionObject ConsumObject {get; set;};
Списки ElectricityMeteringPoint, по каждому объекту можно получить формируя соответствующий запрос к БД. Более подробно на примере, аналогичная проблема разбирается вот здесь Там на примере интернет магазина на ASP.NET Core MVC сначала создается Категория Товара, а потом создается сущность Товар, которая содержит ссылку на существующую Категорию