I am attempting to read related data across three tables in ASP.NET CORE MVC 2.2 with Entity Framework Core.
To be more specific I am using this tutorial to build my project https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/read-related-data?view=aspnetcore-2.2. I am using Eager Loading to read Job.JobTitle all the way from the Problem Index method. Reading this I realize I want to find where Problem navigation property that contains the Job entity of the JobTitle that the Problem is assigned to. But That would be a many to many relationship so I have thrown in a Result composite table to stop that.
From my research, adding the foreign key attributes can help. Which hasn’t. So how can I Render the JobTitle that the problem belongs too in the job index?
Job class:
public class Job { public int ID { get; set; } public string JobTitle { get; set; } public string JobDescription { get; set; } public DateTime JobStartDate {get;set;} public DateTime JobDeadline {get;set;} public bool JobIsComplete{get;set;} public ICollection<Result> Results {get;set;} }
Result class:
public class Result { public int JobID {get;set;} public int ProblemID {get;set;} [ForeignKey("Job")]
public Job Job {get;set;} [ForeignKey("Problem")] public Problem Problem {get;set;} }
Problem class:
public class Problem { public int ID {get;set;} public string ProblemTitle {get;set;} public string ProblemDescription {get;set;} public DateTime ProblemStartDate {get;set;} public string ProblemFileAttachments {get;set;} public int ProblemSeverity {get;set;} public bool ProblemComplete {get;set;}
public ICollection<Result> Result {get;set;} }
Index method of ProblemController (please see comment to find bug):
public async Task<IActionResult> Index(string sortOrder, string currentFilter, string searchString, int? pageNumber) { ViewData["CurrentSort"] = sortOrder; ViewData["ProblemIDSortParm"] = String.IsNullOrEmpty(sortOrder) ? "ProblemID_desc" : ""; ViewData["ProblemTitleSortParm"] = String.IsNullOrEmpty(sortOrder) ? "ProblemTitle_desc" : ""; ViewData["ProblemStartDateSortParm"] = sortOrder == "ProblemStartDate" ? "ProblemStartDate_desc" : "ProblemStartDate"; ViewData["ProblemSeveritySortParm"] = sortOrder == "ProblemSeverity" ? "ProblemSeverity_desc" : "ProblemSeverity"; ViewData["ProblemCompleteSortParm"] = sortOrder == "ProblemComplete" ? "ProblemComplete_desc" : "ProblemComplete"; ViewData["CurrentFilter"] = searchString; //READ RELATED DATA HERE var problems = from p in _context.Problems .Include(p => p.Result) .ThenInclude(j => j.Job.JobTitle) select p; //END OF READ RELATED DATA if(searchString != null) { pageNumber = 1; } else { searchString = currentFilter; } if(!String.IsNullOrEmpty(searchString)) { problems = problems.Where(p => p.ProblemTitle.Contains(searchString) || p.ProblemDescription.Contains(searchString)); } switch (sortOrder) { case "ProblemID_desc": problems = problems.OrderByDescending(p => p.ID); break; case "ProblemTitle_desc": problems = problems.OrderByDescending(p => p.ProblemTitle); break; case "ProblemStartDate": problems = problems.OrderBy(p => p.ProblemStartDate); break; case "ProblemStartDate_desc": problems = problems.OrderBy(p => p.ProblemStartDate); break; case "ProblemSeverity": problems = problems.OrderBy(p => p.ProblemSeverity); break; case "ProblemSeverity_desc": problems = problems.OrderByDescending(p => p.ProblemSeverity); break; case "ProblemCompleteSortParm": problems = problems.OrderBy(p => p.ProblemComplete); break; case "ProblemCompleteSortParm_desc": problems = problems.OrderByDescending(p => p.ProblemComplete); break; default: problems = problems.OrderBy(p => p.ProblemTitle); break; } int pageSize = 20; return View(await PaginatedList<Problem>.CreateAsync(problems.AsNoTracking(), pageNumber ?? 1, pageSize)); }
ProblemIndex view, (please see comment to find where I am having problem):
@model PaginatedList<Pitcher.Models.Problem> @{ ViewData["Title"] = "Problems"; }<h1>Index</h1><p><a asp-action="Create">Create New</a></p> @*COPY AND PASTE THIS TAG HELPER METHOD TEXTBOX CUSTOMIZATION INTO OTHER VIEWS TO ENABLE SEARCHING.*@<form asp-action="Index" method="get"><div class="form-actions no-color"><p> Find by name: <input type="text" name="SearchString" value="@ViewData["currentFilter"]" /><input type="submit" value="Search" button type="button" class="btn btn-primary" /> |<a asp-action="Index">Back to Full List </a></p></div></form><table class="table table-hover"><thead><tr><th><a asp-action="Index" asp-route-sortOrder="@ViewData["ProblemIDSortParm"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">Problem ID</a></th><th><a asp-action="Index" asp-route-sortOrder="@ViewData["ProblemTitleSortParm"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">Problem Title</a></th><th><a asp-action="Index" asp-route-sortOrder="@ViewData["ProblemStartDateSortParm"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">Problem Start Date</a></th><th> Problem File Attachments</th><th><a asp-action="Index" asp-route-sortOrder="@ViewData["ProblemSeveritySortParm"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">ProblemSeverity</a></th><th><a asp-action="Index" asp-route-sortOrder="@ViewData["ProblemCompleteSortParm"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">ProblemComplete</a></th><th> Job Title</th><th></th></tr></thead><tbody> @foreach (var item in Model) {<tr><td> @Html.DisplayFor(modelItem => item.ID)</td><td> @Html.DisplayFor(modelItem => item.ProblemTitle)</td><td> @Html.DisplayFor(modelItem => item.ProblemDescription)</td><td> @Html.DisplayFor(modelItem => item.ProblemStartDate)</td><td> @Html.DisplayFor(modelItem => item.ProblemFileAttachments)</td><td> @Html.DisplayFor(modelItem => item.ProblemSeverity)</td><td> @Html.DisplayFor(modelItem => item.ProblemComplete)</td> @* RENDER Job Title here *@ @Html.DisplayFor(modelItem => item.Result) @* END render Job Title here *@<td><a asp-action="Edit" asp-route-id="@item.ID" button type="button" class="btn btn-primary btn-block" >Edit</a> <a asp-action="Details" asp-route-id="@item.ID" button type="button" class="btn btn-info btn-block">Details</a> <a asp-action="Delete" asp-route-id="@item.ID" button type="button" class="btn btn-primary btn-block">Delete</a></td></tr>}</tbody></table> @{ var prevDisabled = !Model.HasPreviousPage ? "disabled" : ""; var nextDisabled = !Model.HasNextPage ? "disabled" : ""; }<a asp-action="Index" asp-route-sortOrder="@ViewData["CurrentSort"]" asp-route-pageNumber="@(Model.PageIndex - 1)" asp-route-currentFilter="@ViewData["CurrentFilter"]" class="btn btn-secondary @prevDisabled" button type="button"> Previous</a><a asp-action="Index" asp-route-sortOrder="@ViewData["CurrentSort"]" asp-route-pageNumber="@(Model.PageIndex + 1)" asp-route-currentFilter="@ViewData["CurrentFilter"]" class="btn btn-secondary @nextDisabled" button type="button"> Next</a>