Search code examples
c#asp.net-coreentity-framework-corerazor-pages

Foreign Key word display blank when the app is run


I have built a Book app in ASP.NET Core razor pages CRUD using Entity Framework. However, when I run the app and view it on https://localhost:44370/, the Foreign Key word appears blank. Here is my code:

Models Author

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace BookStore.Models
{
    public class Author
{
        public int ID { get; set; }
        
        [StringLength(50)]
        [Display(Name = "Country Name")]
        public string CountryName { get; set; }

        [StringLength(50)]
        [Display(Name = "Author Name")]
        public string AuthorName { get; set; }      

        
        [ForeignKey("AuthorID")]
        public ICollection<Book> Books { get; set; }

        [ForeignKey("AuthorID")]
        public ICollection<Tour> Tours { get; set; }
    }
 }

The Models Book class

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace BookStore.Models
{
    public class Book
    {
        [Key]
        public int BookID { get; set; }

        [StringLength(50)]
        [Column("BookTitle")]
        [Display(Name = "Book Title")]
        public string BookTitle { get; set; }

        [StringLength(50)]
        [Column("BookPublisher")]
        [Display(Name = "Book Publisher")]
        public string BookPublisher { get; set; }        

        [ForeignKey("AuthorID")]
        public Author Author { get; set; }
        public int AuthorID { get; set; }

        
        public ICollection<BookReview> BookReviews { get; set; }
    }
}

Models Tour class

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace BookStore.Models
{
    public class Tour
    {
        [Key]
        public int TourID { get; set; }

        [StringLength(50)]
        [Column("TourName")]
        [Display(Name = "Tour Name")]
        public string TourName { get; set; }

        [StringLength(50)]
        [Column("TourPlace")]
        [Display(Name = "Tour Place")]
        public string TourPlace { get; set; }        

        [ForeignKey("AuthorID")]
        public Author Author { get; set; }
        public int AuthorID { get; set; }

        
        public ICollection<BookRating> BookRating { get; set; }
    }
}

Initially, when I had scaffolded the Pages for the Book Folder and the Tour Folder, the generated Create.cshtml.cs and Edit.cshtml.cs pages showed the Foreign key as "CountryName" and so I modified it to "AuthorName"

Pages:Book:Create.cshtml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using BookStore.Models;

namespace BookStore.Pages.Books
{
    public class CreateModel : PageModel
    {
        private readonly BookStore.Models.BookWorkContext _context;

        public CreateModel(BookStore.Models.BookWorkContext context)
        {
            _context = context;
        }

        public IActionResult OnGet()
        {
        ViewData["AuthorID"] = new SelectList(_context.Author, "ID", "AuthorName");
            return Page();
        }

        [BindProperty]
        public  Book Book { get; set; }

        public async Task<IActionResult> OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            _context.Book.Add(Book);
            await _context.SaveChangesAsync();

            return RedirectToPage("./Index");
        }
    }
}

Data: 'DbInitilializer.cs'

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using BookStore.Models;


namespace BookStore.Data
{
    public class DbInitializer
    {
        public static void Initialize(BookWorkContext context)
        {
            context.Database.EnsureCreated();

            // Look for any Authors.
            if (context.Author.Any())
            {
                return;   // DB has been seeded
            }

            var Authors = new Author[]
            {
                new Author {CountryName="United States of America",AuthorName="John Coltrane"},
                new Author {CountryName= "United States of America",AuthorName= "Jim Meyers"}
            };

            foreach (Author a in Authors)
            {
                context.Author.Add(a);
            }
            context.SaveChanges();

            var Tours = new Tour[]
            {
                new Tour {AuthorID = Authors.Single(a => a.AuthorName == "John Coltrane").ID,TourName = "The Prairie Tour",TourPlace = "Town Center"},
                new Tour {AuthorID = Authors.Single(a => a.AuthorName == "Jim Meyers").ID,TourName = "Santa Barbara Tours",TourPlace = "Long Circular Mall"},
            };

            foreach (Tour t in Tours)
            {
                context.Tour.Add(t);
            }
            context.SaveChanges();

            var Books = new Book[]
            {
                new Book {AuthorID = Authors.Single(a => a.AuthorName == "Tim Hendricks").ID, BookTitle = "Three Stooges",BookPublisher = "Penguin"},
                new Book {AuthorID = Authors.Single(a => a.AuthorName == "Andy Ford").ID, BookTitle = "Tom Sawyer",BookPublisher = "Collins"},
            };

            foreach (Book b in Books)
            {
                context.Book.Add(b);
            }
            context.SaveChanges();
        }
    }
}

On running the app, if I try to create a new entry, I see the AuthorName in the dropdown field but once I save the new entry, the Foreign Key column is blank and the other columns are filled with data.
My question is how do I get the Foreign Key word "AuthorName" to appear in the app once I run the app?

I have attached a link to the Book Razor Page: - Book Razor Page


Solution

  • Here is a solution: -

    1.Model: -

    public class Author
    {
        public int ID { get; set; }
        [StringLength(50)]
        [Display(Name = "Country Name")]
        public string CountryName { get; set; }
    
        [StringLength(50)]
        [Display(Name = "Author Name")]
        public string AuthorName { get; set; }
    
        [ForeignKey("AuthorID")]
        public ICollection<Book> Books { get; set; }
    
        [ForeignKey("AuthorID")]
        public ICollection<Tour> Tours { get; set; }
    }
    public class Book
    {
        [Key]
        public int BookID { get; set; }
        [StringLength(50)]
        [Column("BookTitle")]
        [Display(Name = "Book Title")]
        public string BookTitle { get; set; }
    
        [StringLength(50)]
        [Column("BookPublisher")]
        [Display(Name = "Book Publisher")]
        public string BookPublisher { get; set; }
    
        //  [ForeignKey("AuthorID")]
        public Author Author { get; set; }
        public int AuthorID { get; set; }
    }
    public class Tour
    {
        [Key]
        public int TourID { get; set; }
        [StringLength(50)]
        [Column("TourName")]
        [Display(Name = "Tour Name")]
        public string TourName { get; set; }
    
        [StringLength(50)]
        [Column("TourPlace")]
        [Display(Name = "Tour Place")]
        public string TourPlace { get; set; }
    
        // [ForeignKey("AuthorID")]
        public Author Author { get; set; }
        public int AuthorID { get; set; }
    
    }
    

    2.Create.cshtml: -

    @page
    @model CreateModel
    
    <h4>Book</h4>
    <hr />
    <div class="row">
        <div class="col-md-4">
            <form method="post">
                <div asp-validation-summary="ModelOnly" class="text-danger"></div>
                <div class="form-group">
                    <label asp-for="Book.BookTitle" class="control-label"></label>
                    <input asp-for="Book.BookTitle" class="form-control" />
                    <span asp-validation-for="Book.BookTitle" class="text-danger"></span>
                </div>
                <div class="form-group">
                    <label asp-for="Book.BookPublisher" class="control-label"></label>
                    <input asp-for="Book.BookPublisher" class="form-control" />
                    <span asp-validation-for="Book.BookPublisher" class="text-danger"></span>
                </div>
                <div class="form-group">
                    <label asp-for="Book.AuthorID" class="control-label"></label>
                    <select asp-for="Book.AuthorID" class ="form-control" asp-items="ViewBag.AuthorID"></select>
                </div>
                <div class="form-group">
                    <input type="submit" value="Create" class="btn btn-primary" />
                </div>
            </form>
        </div>
    </div>
    
    @section Scripts {
        @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
    }
    
    

    3.Index.cshtml:

    @page
    @model IndexModel
    <p>
        <a asp-page="Create">Create New</a>
    </p>
    <table class="table">
        <thead>
            <tr>
                <th>
                    @Html.DisplayNameFor(model => model.Book[0].BookTitle)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.Book[0].BookPublisher)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.Book[0].Author)
                </th>
                <th></th>
            </tr>
        </thead>
        <tbody>
    @foreach (var item in Model.Book) {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.BookTitle)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.BookPublisher)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Author.AuthorName)
                </td>
                <td>
                    <a asp-page="./Edit" asp-route-id="@item.BookID">Edit</a> |
                    <a asp-page="./Details" asp-route-id="@item.BookID">Details</a> |
                    <a asp-page="./Delete" asp-route-id="@item.BookID">Delete</a>
                </td>
            </tr>
    }
        </tbody>
    </table>
    

    3.Index.cshtml.cs:

    public class IndexModel : PageModel
    {
        private readonly RazorProj2_2.Models.RazorProj2_2Context _context;
    
        public IndexModel(RazorProj2_2.Models.RazorProj2_2Context context)
        {
            _context = context;
        }
    
        public IList<Book> Book { get;set; }
    
        public async Task OnGetAsync()
        {
            Book = await _context.Book
                .Include(b => b.Author).ToListAsync();
        }
    }