Пытаюсь прочитать из таблицы базы данных, но вывод неверен

#c# #asp.net #entity-framework #asp.net-core #razor-pages

Вопрос:

Вот мой код:

треки.cshtml

 @page
@using Project.Models
@model Project.Pages.TracksModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<div class="row">
    <h1 class="display-2">Tracks</h1>
    <table class="table">
        <thead class="thead-inverse">
            <tr>
                <th>Artist name</th>
                <th>Album name</th>
                <th>Track ID</th>
                <th>Track name</th>
            </tr>
        </thead>

        <tbody>
            @foreach (Track track in Model.Tracks)
            {
                <tr>
                    <td>@Html.DisplayFor(modelArtists => track.Artist.Name)</td>
                    <td>@Html.DisplayFor(modelAlbums => track.Album.Title)</td>
                    <td>@track.TrackId</td>
                    <td>@track.Name</td>
                    <td><a asp-page="/Tracks/Details" asp-route-id="@track.TrackId">Details</a></td>
                    <td><a asp-page="/Tracks/Edit" asp-route-id="@track.TrackId">Edit</a></td>
                    <td><a asp-page="/Tracks/Delete" asp-route-id="@track.TrackId">Delete</a></td>
                </tr>
            }
        </tbody>
    </table>
</div>
<div class="row">
    <p>Enter a name amp; TrackID (over 3510) for a new track:amp;nbsp;</p>
    <form method="POST">
        <div><input asp-for="Track.Name" /></div>
        <div><input asp-for="Track.TrackId" /></div>
        <input type="submit" />
    </form>
</div>
<div>
    <a asp-page="/Index">Home</a>
</div>
 

треки.cshtml.cs

 using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using Project.Models;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Project.Pages
{
    public class TracksModel : PageModel
    {
        private Chinook db;

        public TracksModel(Chinook injectedContext)
        {
            db = injectedContext;
        }
        public IEnumerable<Track> Tracks { get; set; }
        public void OnGetAsync()
        {
            ViewData["Title"] = "Chinook Web Site - Tracks";
            Tracks = db.Tracks.Include(a => a.Album)
            .Include(a => a.Artist);
        }
        [BindProperty]
        public Track Track { get; set; }

        public IActionResult OnPost()
        {
            if (ModelState.IsValid)
            {
                db.Tracks.Add(Track);
                db.SaveChanges();
                return RedirectToPage("/tracks");
            }
            return Page();
        }

        public IActionResult DeleteTrack(int TrackId)
        {
            var track = db.Tracks.Find(TrackId);

            if (track == null) return Page();

            db.Tracks.Remove(track); db.SaveChanges(); return RedirectToPage("/tracks");
        }
    }
}
 

Репо на GitHub

Что-то не так, так как страница, похоже, повторяет имена исполнителей несколько раз, до такой степени, что все отображаемые имена исполнителей начинаются с буквы A. Я должен сказать, что способ, которым я настроил базу данных, ArtistId является внешним ключом Album таблицы, в то время AlbumId как это внешний ключ Tracks таблицы.

Может быть, мне нужны такие пункты, как .Where(a => a.AlbumId == Tracks.AlbumId) и .Where(a => a.ArtistId == Album.ArtistId) ? Я пытался написать что-то подобное, но, должно быть, я сделал это неправильно, так как это просто полностью остановило отображение треков. Какая-нибудь помощь, пожалуйста? ТИА

Правка: Я замечаю, что в то время как названия моих альбомов объединены из Album таблицы, имена моих исполнителей-нет, хотя метод, по сути, тот же самый. Мне приходит в голову, что, хотя Album таблица напрямую подключена к Track таблице (через AlbumId внешний ключ), Artist таблица не подключена. Это только косвенно связано с таблицей «Трек», через Album таблицу».

Artist —- Album —- Track

Edit2: Мне сказали, что Where в данном случае это не нужно, и что я должен использовать «ThenInclude», поэтому в настоящее время у меня есть

    public void OnGetAsync()
    {
        ViewData["Title"] = "Chinook Web Site - Tracks";
        Tracks = db.Tracks.Include(a => a.Album).ThenInclude(b => b.Artist).ToList();
    }
 

Однако это все равно приводит к той же самой ошибке.

Комментарии:

1. Возможно, вы могли бы предоставить статический результат html для проверки того, что не так.

2. Извините, я не совсем понимаю, что вы имеете в виду. Но если вы посмотрите на этот скриншот, возможно, вы увидите, что, по-видимому, AC/DC выпускает слишком много релизов. i.imgur.com/fYMZXFL.jpg Это неверно. Имя исполнителя как-то неправильно повторяется.

3. Вы проверили правильность внешнего ключа в базе данных? Потому что, похоже, Aerosmith тоже становится ACDC. Или вы отладили его и проверили, что он работает правильно, прежде чем попасть в цикл?

4. Извините, я не совсем понимаю, что вы имеете в виду? Я могу заставить имена исполнителей нормально отображаться на других страницах моего сайта, только на этой странице у меня возникла эта проблема.

Ответ №1:

Ключевой момент заключается в Context.Chinook.cs

 protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            ....

            modelBuilder.Entity<Track>()
            .ToTable("tracks")
            .HasOne(mtype => mtype.Artist)
            .WithMany(trk => trk.Tracks)
            .HasForeignKey(mtype => mtype.AlbumId)
            .HasForeignKey(mtype => mtype.GenreId)
            .HasForeignKey(mtype => mtype.MediaTypeId)
            .OnDelete(DeleteBehavior.NoAction);
        }
 

Цепочки HasForeignKey должны быть разделены, если они не связаны с вышеуказанным отношением(1 к n, n к 1, 1 к 1).
Ниже приведены коды, которые я изменил.

Контекст.Chinook.cs

 protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Album>()
            .ToTable("albums")
                    .HasOne(al => al.Artist)
                .WithMany(ar => ar.Albums)
                .HasForeignKey(al => al.ArtistId);

            modelBuilder.Entity<Artist>().ToTable("artists");

            modelBuilder.Entity<Media_type>()
            .ToTable("media_types")
            .HasKey(c => c.MediaTypeId);

            modelBuilder.Entity<Genre>().ToTable("genres");

            modelBuilder.Entity<Track>()
            .ToTable("tracks")
            .HasOne(trk => trk.Album)
            .WithMany(trk => trk.Tracks)
            .HasForeignKey(trk => trk.AlbumId)
             .OnDelete(DeleteBehavior.NoAction);

            modelBuilder.Entity<Track>()
            .ToTable("tracks")
            .HasOne(trk => trk.Genre)
            .WithMany(trk => trk.Tracks)
            .HasForeignKey(trk => trk.GenreId);

            modelBuilder.Entity<Track>()
            .ToTable("tracks")
            .HasOne(trk => trk.Media_type)
            .WithMany(trk => trk.Tracks)
           .HasForeignKey(mtype => mtype.MediaTypeId);
        }
 

Сущности.Художник

 public class Artist
{
     public int ArtistId { get; set; }
     public string Name { get; set; }

     public ICollection<Album> Albums { get; set; }
}
 

Сущности.Жанр

 public class Genre
{
    public int GenreId { get; set; }
    public string Name { get; set; }

    public ICollection<Track> Tracks { get; set; }
}
 

Сущности.Media_type

 public class Media_type
{
    public  int MediaTypeId { get; set; }
    public  string Name { get; set; }

    public ICollection<Track> Tracks { get; set; }
}
 

Сущности.Дорожка

     public class Track
    {
        public int TrackId { get; set; }
        public string Name { get; set; }
        public int? AlbumId { get; set; }
        public int? MediaTypeId { get; set; }
        public int? GenreId { get; set; }
        public string Composer { get; set; }
        public int? Milliseconds { get; set; }
        public int? Bytes { get; set; }
        public int? UnitPrice { get; set; }

        public Album Album { get; set; }
        public Genre Genre { get; set; }
        public Media_type Media_type { get; set;}
    }
 

Веб-приложение.треки.cshtml.cs

    public void OnGetAsync()
   {
        ViewData["Title"] = "Chinook Web Site - Tracks";

        Tracks = db.Tracks
        .Include(a => a.Album)
        .ThenInclude(a => a.Artist);
   }
 

WebApp.tracks.cshtml

    <tbody>
                @foreach (Track track in Model.Tracks)
                {
                    <tr>
                        <td>@Html.DisplayFor(modelArtists => track.Album.Artist.Name)</td>
                        <td>@Html.DisplayFor(modelAlbums => track.Album.Title)</td>
                        <td>@track.TrackId</td>
                        <td>@track.Name</td>
                        <td><a asp-page="/Tracks/Details" asp-route-id="@track.TrackId">Details</a></td>
                        <td><a asp-page="/Tracks/Edit" asp-route-id="@track.TrackId">Edit</a></td>
                        <td><a asp-page="/Tracks/Delete" asp-route-id="@track.TrackId">Delete</a></td>
                    </tr>
                }
</tbody>
 

Ссылка:
Основные отношения Entity Framework