Quantcast
Channel: ASP.NET Core
Viewing all articles
Browse latest Browse all 9386

Field not rendering across 3 table relationship

$
0
0

Hi So I am using ASP.NET Core 2.2 with EF Core with SQL Server DB. I got a database that looks like this. So I have 3 tables in SQL Server. tblJobs, tblResults and tblProblems and others I omitted as they are not part of the problem.

Entity Relationship Diagram and gif demo of problem

I am attempting to read related data across these three tables. I am using Eager Loading to read the model field "Job.JobTitle" in the "ProblemsController.Index" method with this tutorial. https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/read-related-data?view=aspnetcore-2.2

I decided to manually insert in a query window into Result table the JobID matching the JobTitle I wanted with the ProblemID. It has now rendered the correct JobTitle in the Problem Index view record which is the value of "Pitcher". But that's only because I manually inserted it in tblResult which doesn't really help the end user. I'm wondering what's a way of them getting past that manual insertion.

Here is the my TeamContext class.

using Pitcher.Models;
using Microsoft.EntityFrameworkCore;
using Pitcher.Models.TeamViewModels;

namespace Pitcher.Data
{
    public class TeamContext : DbContext
    {
        public TeamContext(DbContextOptions<TeamContext> options) : base(options)
        {
        }

        public DbSet<User> Users { get; set; }
        public DbSet<Registration> Registrations {get;set;}
        public DbSet<Job> Jobs {get;set;}     

        public DbSet<Problem> Problems { get; set; }   

        public DbSet<Result> Results {get;set;}
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<User>().ToTable("tblUser");
            modelBuilder.Entity<Registration>().ToTable("tblRegistration");
            modelBuilder.Entity<Job>().ToTable("tblJob");
            modelBuilder.Entity<Problem>().ToTable("tblProblem");
            modelBuilder.Entity<Chat>().ToTable("tblChat");
            modelBuilder.Entity<Result>().ToTable("tblResult");

            modelBuilder.Entity<Result>()
                .HasKey(bc => new { bc.JobID, bc.ProblemID });
            modelBuilder.Entity<Result>()
                .HasOne(bc => bc.Job)
                .WithMany(b => b.Results)
                .HasForeignKey(bc => bc.JobID);
            modelBuilder.Entity<Result>()
                .HasOne(bc => bc.Problem)
                .WithMany(c => c.Result)
                .HasForeignKey(bc => bc.ProblemID);
        }        
    }
}

Here are the 3 models.

Job model:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;

namespace Pitcher.Models
{
    public class Job
    {        
        
        public int ID { get; set; }

        [Required]
        [StringLength(20, MinimumLength = 3, ErrorMessage = "Job Title must be bettween 3 to 20 characters.")]
        [DataType(DataType.Text)]
        [Display(Name = "Job Title")]
        [Column("JobTitle")]
        public string JobTitle { get; set; }

        [StringLength(200, MinimumLength = 3, ErrorMessage = "Job Description must be bettween 200 to 3 characters.")]
        [DataType(DataType.Text)]
        [Display(Name = "Description")]
        [Column("JobDescription")]
        public string JobDescription { get; set; }

        [Required]
        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        [Display(Name = " Start Date")]
        [Column("JobStartDate")]
        public DateTime JobStartDate {get;set;}

        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        [Display(Name = "Deadline Date")]
        [Column("JobDeadlineDate")]
        public DateTime JobDeadline {get;set;}

        [Display(Name = "Job Is Complete?")]
        [Column("JobIsComplete")]
        public bool JobIsComplete{get;set;}

        public ICollection<Registration> Registrations {get;set;}

        public ICollection<Result> Results {get;set;}
    }
}

Result model:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Pitcher.Models
{
    public class Result
    {        
        public int JobID {get;set;}
        
        public int ProblemID {get;set;}
        public Job Job {get;set;}
        public Problem Problem {get;set;}

    }
}

Problem model:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;

namespace Pitcher.Models
{
    public class Problem
    {
        public int ID {get;set;}
        
        [Required]
        [StringLength(180, MinimumLength = 2, ErrorMessage = "Problem Title must be bettween 2 to 20 characters.")]
        [DataType(DataType.Text)]
        [Display(Name = "Problem Title")]
        [Column("ProblemTitle")]
        public string ProblemTitle {get;set;}

        [Required]
        [StringLength(int.MaxValue, MinimumLength = 5, ErrorMessage = "Problem Title must be at least 5 characters.")]
        [DataType(DataType.Text)]
        [Display(Name = "Problem Description")]
        [Column("ProblemDescription")]
        public string ProblemDescription {get;set;}

        [Required]
        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        [Display(Name = " Problem Start Date")]
        [Column("ProblemStartDate")]
        public DateTime ProblemStartDate {get;set;}

        [DataType(DataType.Upload)]
        [Display(Name = " Upload file")]
        [Column("ProblemFileAttachments")]
        public string ProblemFileAttachments {get;set;}

        [Required]
        [Display(Name = "Problem Severity")] 
        [Range(1,5, ErrorMessage
             = "Problem Severity value for {0} must be between {1} and {2}.")]       
        [Column("ProblemSeverity")]
        public int ProblemSeverity {get;set;}

        [Display(Name = "Problem Complete")]        
        [Column("ProblemComplete")]        
        public bool ProblemComplete {get;set;}
        public ICollection<Result> Result {get;set;}

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

Here are the 2 Controllers I'm using with their Index methods only.

Job Index controller method:

        public async Task<IActionResult> Index(string sortOrder, string currentFilter, string searchString, int? pageNumber)
        {
            ViewData["CurrentSort"] = sortOrder;
            ViewData["JobTitleSortParm"] = sortOrder == "JobStartDate" ? "JobTitle_desc" : "JobStartDate";
            ViewData["JobStartDateSortParm"] = sortOrder == "JobStartDate" ? "JobStart_date_desc" : "JobStartDate";
            ViewData["JobDeadlineDateSortParm"] = sortOrder == "JobDeadlineDate" ? "JobDeadline_date_desc" : "JobDeadlineDate";
            ViewData["CurrentFilter"] = searchString;
            var jobs = from j in _context.Jobs
                        select j;

            if (searchString != null)
            {
                pageNumber = 1;
            }
            else
            {
                searchString = currentFilter;
            }


            if (!String.IsNullOrEmpty(searchString))
            {
                jobs = jobs.Where(j => j.JobTitle.Contains(searchString)
                                    || j.JobDescription.Contains(searchString));
            }

            switch (sortOrder)
            {
                case "JobTitle_desc":
                    jobs = jobs.OrderByDescending(j => j.JobTitle);
                    break;
                case "JobStartDate":
                    jobs = jobs.OrderBy(j => j.JobStartDate);
                    break;
                case "JobStart_date_desc":
                    jobs = jobs.OrderByDescending(j => j.JobStartDate);
                    break;
                case "JobDeadline_date_desc":
                    jobs = jobs.OrderByDescending(j => j.JobDeadline);
                    break;
                case "JobDeadlineDate":
                    jobs = jobs.OrderBy(j => j.JobDeadline);
                    break;
                //By default JobTitle is in ascending order when entity is loaded. 
                default:
                    jobs = jobs.OrderBy(j => j.JobTitle);
                    break;                    
            } 

            int pageSize = 20;
            return View(await PaginatedList<Job>.CreateAsync(jobs.AsNoTracking(), pageNumber ?? 1, pageSize));
        }

Problem Index controller method:

public async Task<IActionResult> Index(string sortOrder, string currentFilter, string searchString, int? pageNumber)
        {
            ViewData["CurrentSort"] = sortOrder;
            ViewData["ProblemIDSortParm"]  = sortOrder == "ProblemID" ? "ProblemID_desc" : "ProblemID";
            ViewData["ProblemTitleSortParm"] = sortOrder == "ProblemTitle" ? "ProblemTitle_desc" : "ProblemTitle";
            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)                                                     
                                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 "ProblemTitle":
                    problems = problems.OrderBy(p => p.ProblemTitle);
                    break;
                case "ProblemStartDate":
                    problems = problems.OrderBy(p => p.ProblemStartDate);                    
                    break;
                case "ProblemStartDate_desc":
                    problems = problems.OrderByDescending(p => p.ProblemStartDate);                    
                    break;
                case "ProblemSeverity":
                    problems = problems.OrderBy(p => p.ProblemSeverity);
                    break;
                case "ProblemSeverity_desc":
                    problems = problems.OrderByDescending(p => p.ProblemSeverity);
                    break;   
                case "ProblemComplete":
                    problems = problems.OrderBy(p => p.ProblemComplete);
                    break;
                case "ProblemComplete_desc":
                    problems = problems.OrderByDescending(p => p.ProblemComplete);
                    break; 
                default:
                    problems = problems.OrderBy(p => p.ID);
                    break;                 
            }

            int pageSize = 20;            
            return View(await PaginatedList<Problem>.CreateAsync(problems.AsNoTracking(), pageNumber ?? 1, pageSize));
        }

And the Index view for Problem:

@model PaginatedList<Pitcher.Models.Problem>

@{
    ViewData["Title"] = "Problems";
}<h1>Problems</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>
                Description</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><td>
                @foreach (var title in item.Result)
                {
                    @Html.DisplayFor(modelItem => title.Job.JobTitle)<br />
                }</td><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>

Kind regards,

Jordan Nash


Viewing all articles
Browse latest Browse all 9386

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>