Here is my code:
tracks.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 & TrackID (over 3510) for a new track: </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>
tracks.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");
}
}
}
There is something wrong as the page appears to repeat the artist names multiple times, to such an extent that all the artists names that are displayed begin with the letter A. I should say that the way I have the database set up, ArtistId
is a foreign key of the Album
table, while AlbumId
is a foreign key of the Tracks
table.
Maybe I need clauses like .Where(a => a.AlbumId == Tracks.AlbumId)
and .Where(a => a.ArtistId == Album.ArtistId)
? I tried to write something like that, but I must have done it wrong as it just stopped the tracks from displaying altogether. Any help please? TIA
Edit: I'm noticing that whereas my Album titles are joined from the Album
table, my Artist names are not, even though the method is essentially just the same. It occurs to me that whereas the Album
table is directly connected to the Track
table (through the AlbumId
foreign key), the Artist
table is not. It is only indirectly related to the 'Track' table, through the Album
table`.
Artist
----Album
----Track
Edit2: I was told that Where
is not needed in this instance, and that I should use 'ThenInclude`, so I've currently got
public void OnGetAsync()
{
ViewData["Title"] = "Chinook Web Site - Tracks";
Tracks = db.Tracks.Include(a => a.Album).ThenInclude(b => b.Artist).ToList();
}
It's still resulting in the exact same error though.
The key point is at 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 chains must to be separated if they are not related to above relation(1 to n, n to 1, 1 to 1). The followings are that codes I have modified.
Context.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);
}
Entities.Artist
public class Artist
{
public int ArtistId { get; set; }
public string Name { get; set; }
public ICollection<Album> Albums { get; set; }
}
Entities.Genre
public class Genre
{
public int GenreId { get; set; }
public string Name { get; set; }
public ICollection<Track> Tracks { get; set; }
}
Entities.Media_type
public class Media_type
{
public int MediaTypeId { get; set; }
public string Name { get; set; }
public ICollection<Track> Tracks { get; set; }
}
Entities.Track
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;}
}
WebApp.tracks.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>
Reference: Entity Framework Core Relationships