Получение данные из таблицы. Web API. ASP.NET CORE из базы данных MS SQL Server

Я начал создавать свой собственный веб сервис. Используемые технологии (ASP.NET CORE Web API, Entity framework core) а также для соединение базу данный использовал MS SQL Server.

Первое, что делал это создал модель, репозиторий а также контекст базу данных и там соединил таблицы между собой и мигрировал их, а потом перенёс все библиотеку классов и там ещё раз мигрировал

Второе, Создал контроллеры для использование операцию CRUD. И теперь возникает проблема, когда вызываю контроллер определенного таблицы, данные с другого таблицы не получаю, может быть я где-то накосячил. Можете найти где именно проблемы. Заранее спасибо.

WebServiceContext.cs

using ClassLibrary.Contexts_Repositories_Models.Models;
using Microsoft.EntityFrameworkCore;

namespace ClassLibrary.Contexts_Repositories_Models.Context;

public class WebServiceContext: DbContext
{
    public WebServiceContext() { }

    public WebServiceContext(DbContextOptions<WebServiceContext> options): base(options)
    {
        Database.EnsureDeleted();
        Database.EnsureCreated();
    }

    public DbSet<Airport> Airports { get; set; }
    public DbSet<CodeAirport> CodeAirports { get; set; }
    public DbSet<DepartureAndArrival> DeparturesAndArrivals { get; set; }
    public DbSet<Information> Informations { get; set; }
    public DbSet<PitStop> PitStops { get; set; }


    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer("Server=HP-NETBOOK-WIN\\SQLEXPRESS;Database=itvdnDB;User id=itvdn;Password=1;Trusted_Connection=True;TrustServerCertificate=True");
    }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {

        /// Airports Table
        modelBuilder.Entity<Airport>(entity =>
        {
            entity.Property(x => x.Id).IsRequired();
            entity.HasKey(x => x.Id);

            entity.Property(x => x.Country).IsRequired();
            entity.Property(x => x.City).IsRequired();
            entity.Property(x => x.NameAirport).IsRequired();
            entity.Property(x => x.Airline).IsRequired();
            entity.Property(x => x.Active).IsRequired();

            ///Airport-Information, One-To-One
            entity.HasOne(a => a.Information)
            .WithOne(i => i.Airport)
            .HasForeignKey<Information>(a => a.AirportId);


            ///Airport-CodeAirport, One-To-Many
            entity.HasMany(a => a.CodeAirports)
            .WithOne(c => c.Airport)
            .HasForeignKey(c => c.AirportId);



            ///Airport-DepartureAndArrival, One-To-Many
            entity.HasMany(a => a.DepartureAndArrivals)
            .WithOne(da => da.Airport)
            .HasForeignKey(da => da.AirportId);


            ///Airport-PitStop, One-To-Many
            entity.HasMany(a => a.PitStops)
            .WithOne(p => p.Airport)
            .HasForeignKey(p => p.AirportId);

            entity.ToTable("Airports");
        });


        //CodeAirports Table
        modelBuilder.Entity<CodeAirport>(entity =>
        {
            entity.Property(x => x.Id).IsRequired();
            entity.HasKey(x => x.Id);

            entity.Property(x => x.Type).IsRequired();
            entity.Property(x => x.Code).IsRequired();

            entity.ToTable("CodeAirports");
        });


        ///Informations Table
        modelBuilder.Entity<Information>(entity =>
        {
            entity.Property(x => x.Id).IsRequired();
            entity.HasKey(x => x.Id);

            entity.Property(x => x.NumberBoard).IsRequired();
            entity.Property(x => x.NumberFlight).IsRequired();

            entity.ToTable("Informations");
        });


        ///DepartureAndArrivals Table
        modelBuilder.Entity<DepartureAndArrival>(entity =>
        {
            entity.Property(x => x.Id).IsRequired();
            entity.HasKey(x => x.Id);

            entity.Property(x => x.Departure).IsRequired();
            entity.Property(x => x.Arrival).IsRequired();
            entity.Property(x => x.Routing).IsRequired();
            entity.Property(x => x.Distance).IsRequired();

            entity.HasMany(da => da.PitStops)
            .WithOne(ps => ps.DepartureAndArrival)
            .HasForeignKey(ps => ps.DepartureAndArrivalId);

            entity.ToTable("DeparturesAndArrivals");
        });


        //PitStops Table
        modelBuilder.Entity<PitStop>(entity =>
        {
            entity.Property(x => x.Id).IsRequired();
            entity.HasKey(x => x.Id);
            entity.Property(x => x.PitStopCity).IsRequired();

            entity.ToTable("PitStops");
        });
    }
}

Airport.cs

namespace ClassLibrary.Contexts_Repositories_Models.Models;

public class Airport
{
    public int Id { get; set; }
    public string? Country {  get; set; }
    public string? City { get; set; }
    public string? NameAirport {  get; set; }
    public string? Airline {  get; set; }
    public bool Active {  get; set; }
    public Information? Information { get; set; }
    public List<CodeAirport> CodeAirports { get; set; }
    public List<PitStop> PitStops { get; set; }
    public List<DepartureAndArrival> DepartureAndArrivals { get; set; }


    public Airport()
    {
        CodeAirports = new List<CodeAirport>();
        PitStops = new List<PitStop>();
        DepartureAndArrivals = new List<DepartureAndArrival>();
    }
}

AirportRepository.cs

using ClassLibrary.Contexts_Repositories_Models.Context;
using ClassLibrary.Contexts_Repositories_Models.Models;
using Microsoft.EntityFrameworkCore;

namespace ClassLibrary.Contexts_Repositories_Models.Repositories;

public class AirportRepository : IRepository<Airport>
{
    #region FIELDS

    private WebServiceContext dbContext;
    private bool disposed = false;
    #endregion
    #region CONSTRUCTOR
    public AirportRepository(WebServiceContext dbContext) => this.dbContext = dbContext;
    #endregion
    #region METHODS


    /// <summary>
    /// Метод получение коллекция объекта типа Airport
    /// </summary>
    /// <returns>Возвращает коллекция объекта типа Airport</returns>
    public IEnumerable<Airport> GetAll()
    {
        foreach(var airport in  dbContext.Airports)
        {
            if (airport.CodeAirports.Count==0)
                throw new Exception("CodeAirports type collection is null!!!!");
            else if (airport.PitStops.Count == 0)
                throw new Exception("PitStops type collection is null!!!!");
            else if(airport.DepartureAndArrivals.Count == 0)
                throw new Exception("DepartureAndArrivals type collection is null!!!!");
            if (airport.Information == null)
                throw new Exception("Information type is null!!!!");
        }
        return dbContext.Airports;
    }


    /// <summary>
    /// Метод получение объекта типа Airport по id
    /// </summary>
    /// <param name="id">id объекта типа Airport</param>
    /// <returns>Возвращает объект типа Airport</returns>
    public Airport GetById(int id)
    {
        Airport? airport = dbContext.Airports.FirstOrDefault(x => x.Id == id);
        if (airport == null)
            return null!;
        return airport;
    }


    /// <summary>
    /// Создание объекта типа Airport, а также добавление в таблицу Airports из базы данных
    /// </summary>
    /// <param name="airport">Объект типа Airport</param>
    public void Create(Airport airport)
    {
        dbContext.Airports.Add(airport);
        dbContext.SaveChanges();
    }

    /// <summary>
    /// Метод, изменение объекта типа Airport, а также сохранение в таблицу Airports из базы данных
    /// </summary>
    /// <param name="airport">Объект типа Airport</param>
    public void Update(Airport airport)
    {
        dbContext.Entry(airport).State = EntityState.Modified;
        dbContext.SaveChanges();
    }


    /// <summary>
    /// Метод удаление объекта типа Airport, а также изменение в таблицы Airports из базы данных
    /// </summary>
    /// <param name="id">id объекта типа Airport</param>
    public void Delete(int id)
    {
        Airport? airport = GetById(id);
        if (airport != null)
        {
            dbContext.Airports.Remove(airport);
            dbContext.SaveChanges();
        }
    }

    /// <summary>
    /// Виртуальный метод, для удаление контекст базы данных
    /// </summary>
    /// <param name="disposing">Для вызова в контексте базы данных метод,Dispose в интерфейсе IDisposable</param>
    public virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                dbContext.Dispose();
            }
        }
        disposed = true;
    }

    /// <summary>
    /// Метод для вызова виртуального метода Dispose, а также вызов сборщика мусора (Garbage Collector)
    /// </summary>
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    #endregion
}

Результат запроса Web API и отображение на Swagger UI

AirportAPIController.cs

using ClassLibrary.Contexts_Repositories_Models.Context;
using ClassLibrary.Contexts_Repositories_Models.Models;
using ClassLibrary.Contexts_Repositories_Models.Repositories;
using Microsoft.AspNetCore.Mvc;

namespace ITVDN_WEBAPI_PRACTICE.Controllers;

[Route("api/[controller]")]
[ApiController]
public class AirportAPIController : ControllerBase
{
    #region FIELDS
    private IRepository<Airport> _repository;
    private readonly ILogger<AirportAPIController> logger;
    #endregion
    #region CONSTRUCTOR
    public AirportAPIController(ILogger<AirportAPIController> logger)
    {
        _repository = new AirportRepository(new WebServiceContext());
        this.logger = logger;
    }
    #endregion
    #region METHODS

    /// <summary>
    /// Метод, получение коллекцию класса Airport в асинхронном запросе (GET)
    /// </summary>
    /// <returns>Возвращает асинхронный обобщенный тип класса Task. Внутри обобщенного класса Task, 
    /// имеется другой обобщенный класс ActionResult. А внутри обобщенного класса ActionResult, возвращает
    /// обобщенную коллекцию типа IEnumerable, внутри которого тип класса Airport</returns>
    [HttpGet]
    public async Task<ActionResult<IEnumerable<Airport>>> GetAllAirports()
        => await Task.Run(() => Ok(_repository.GetAll()));


    /// <summary>
    /// Метод, получение объекта класса Airport, через id в асинхронном запросе (GET)
    /// </summary>
    /// <param name="id">id объекта класса Airport</param>
    /// <returns>Возвращает асинхронный обобщенный тип класса Task. Внутри обобщенного класса Task,
    /// имеется другой обобщенный класс ActionResult. А внутри обобщенного класса ActionResult, возвращает
    /// объект класса Airport
    /// </returns>
    [HttpGet("{id:int}")]
    public async Task<ActionResult<Airport>> GetAirportByIdAsync(int id)
    {
        if (id < 0)
        {
            logger.LogError("id cannot be negative");
            return await Task.Run(() => BadRequest("id cannot be negative"));
        }
        var airport = _repository.GetById(id);

        if (airport.Information == null)
        {
            logger.LogError("Information is null!!!");
            return await Task.Run(() => NotFound("Information is null!!!"));
        }
        else if (airport.CodeAirports == null)
        {
            logger.LogError("CodeAirports is null!!!");
            return await Task.Run(() => NotFound("CodeAirports is null!!!"));
        }
        else if (airport.DepartureAndArrivals == null)
        {
            logger.LogError("DepartureAndArrivals is null!!!");
            return await Task.Run(() => NotFound("DepartureAndArrivals is null!!!"));
        }
        else if (airport.PitStops == null)
        {
            logger.LogError("PitStops is null!!!");
            return await Task.Run(() => NotFound("PitStops is null!!!"));
        }
        else
        {
            logger.LogError("airport is null!!!");
            return await Task.Run(() => NotFound("airport is null!!!"));
        }

        return await Task.Run(() => Ok(airport));
    }


    /// <summary>
    /// Метод, добавление объекта класса Airport, в таблицу Airports, в асинхронном запросе (POST)
    /// </summary>
    /// <param name="airport">Объект класса Airport</param>
    /// <returns>Возвращает созданный объект асинхронный обобщенный тип класса Task. Внутри обобщенного класса Task,
    /// имеется другой обобщенный класс ActionResult. А внутри обобщенного класса ActionResult, возвращает
    /// объект класса Airport
    /// </returns>
    [HttpPost]
    public async Task<ActionResult<Airport>> CreateAirportAsync([FromQuery] Airport airport)
    {
        if (airport.Information == null)
        {
            logger.LogError("Information is null!!!");
            return await Task.Run(() => NotFound("Information is null!!!"));
        }
        else if (airport.CodeAirports == null)
        {
            logger.LogError("CodeAirports is null!!!");
            return await Task.Run(() => NotFound("CodeAirports is null!!!"));
        }
        else if (airport.DepartureAndArrivals == null)
        {
            logger.LogError("DepartureAndArrivals is null!!!");
            return await Task.Run(() => NotFound("DepartureAndArrivals is null!!!"));
        }
        else if (airport.PitStops == null)
        {
            logger.LogError("PitStops is null!!!");
            return await Task.Run(() => NotFound("PitStops is null!!!"));
        }
        else
        {
            logger.LogError("airport is null!!!");
            return await Task.Run(() => NotFound("airport is null!!!"));
        }

        _repository!.Create(airport);
        return await Task.Run(() => Ok(airport));
    }


    /// <summary>
    /// Метод, изменение объекта класса Airport из таблицы Airports, в асинхронном запросе (PUT)
    /// </summary>
    /// <param name="airport">Объект класса Airport</param>
    /// <returns>Возвращает асинхронный обобщенный тип класса Task. Внутри обобщенного класса Task,
    /// имеется другой обобщенный класс ActionResult. А внутри обобщенного класса ActionResult, возвращает
    /// объект класса Airport
    /// </returns>
    [HttpPut]
    public async Task<ActionResult<Airport>> UpdateAirportAsync([FromQuery] Airport airport)
    {
        if (airport.Information == null)
        {
            logger.LogError("Information is null!!!");
            return await Task.Run(() => NotFound("Information is null!!!"));
        }
        else if (airport.CodeAirports == null)
        {
            logger.LogError("CodeAirports is null!!!");
            return await Task.Run(() => NotFound("CodeAirports is null!!!"));
        }
        else if (airport.DepartureAndArrivals == null)
        {
            logger.LogError("DepartureAndArrivals is null!!!");
            return await Task.Run(() => NotFound("DepartureAndArrivals is null!!!"));
        }
        else if (airport.PitStops == null)
        {
            logger.LogError("PitStops is null!!!");
            return await Task.Run(() => NotFound("PitStops is null!!!"));
        }
        else
        {
            logger.LogError("airport is null!!!");
            return await Task.Run(() => NotFound("airport is null!!!"));
        }

        _repository.Update(airport);
        return await Task.Run(() => Ok(airport));
    }
    /// <summary>
    /// Метод, удаление объекта класса Airport через id из таблицы Airports, в аснхронном запросе (DELETE)
    /// </summary>
    /// <param name="id">id объекта класса Airport</param>
    /// <returns>Возвращает асинхронный обобщенный тип класса Task. Внутри обобщенного класса Task,
    /// имеется другой обобщенный класс ActionResult. А внутри обобщенного класса ActionResult, возвращает
    /// объект класса Airport
    /// </returns>
    [HttpDelete("{id:int}")]
    public async Task<ActionResult<Airport>> DeleteAirportAsync(int id)
    {
        if (id < 0)
        {
            logger.LogError("id cannot be negative");
            return await Task.Run(() => BadRequest("id cannot be negative"));
        }
        Airport airport = _repository.GetById(id);

        if (airport.Information == null)
        {
            logger.LogError("Information is null!!!");
            return await Task.Run(() => NotFound("Information is null!!!"));
        }
        else if (airport.CodeAirports == null)
        {
            logger.LogError("CodeAirports is null!!!");
            return await Task.Run(() => NotFound("CodeAirports is null!!!"));
        }
        else if (airport.DepartureAndArrivals == null)
        {
            logger.LogError("DepartureAndArrivals is null!!!");
            return await Task.Run(() => NotFound("DepartureAndArrivals is null!!!"));
        }
        else if (airport.PitStops == null)
        {
            logger.LogError("PitStops is null!!!");
            return await Task.Run(() => NotFound("PitStops is null!!!"));
        }
        else
        {
            logger.LogError("airport is null!!!");
            return await Task.Run(() => NotFound("airport is null!!!"));
        }

        _repository.Delete(id);
        return await Task.Run(() => Ok(airport));
    }
    #endregion
}

введите сюда описание изображения


Ответы (1 шт):

Автор решения: Faraday

При любой попытке получения данных, для "присоединения" других таблиц вам нужно использовать метод .Include с указанием таблицы, которую хотите включить при запросе. Подробнее просто метод Include тут

В вашем случае, вам нужно изменить выши запросы, внутри репозитория, примерно вот так:

public Airport GetById(int id)
{
    Airport? airport = dbContext.Airports
    .Include(x => x.Information)
    .Include(x => x.CodeAirports)
    .Include(x => x.PitStops)
    .Include(x => x.DepartureAndArrivals)
    .FirstOrDefault(x => x.Id == id);

    if (airport == null)
    {
        return null!;
    }

    return airport;
}

С именами могут быть ошибки, перепысывал вручную. Для остальных методов с получением данных сделайте аналогично, включив нужные вам таблицы

→ Ссылка