0

I am developing a simple website where there are students and there are courses. What is interesting is I can't seem to add a course to my student and make it save. I call the savechanges() method but on my cshtml page, my viewbag does not contain the updated student. Here is what I mean.

enter image description here

Here you can see my ViewBag.Student contains a valid student that has been modified to include two courses under PendingCourses. However, when I load my cshtml page, my ViewBag.Student contains:

enter image description here

So there is definitely some disconnect although I can't figure out what it is. So basically my Details page sets the ViewBag by doing a where statement on the context. This context is modified and saved with the AddCourseToStudent function and then the ViewBag.Student is read in the cshtml. Any help is much appreciated. Below are my files for reference.

Details Function:

// GET: Student/Details/5
    [Authorize]
    public ActionResult Details(int id)
    {
         using (var context = new StudentDbContext())
         {
             ViewBag.Student = context.Students.Where(u => u.id == id).FirstOrDefault();
         }
         var context2 = new ApplicationDbContext();
         var userList = context2.Users.OrderBy(r => r.UserName).ToList().Select(rr => new SelectListItem { Value = rr.UserName.ToString(), Text = rr.UserName }).ToList();
         ViewBag.Users = userList;
        return View();
    }

CSHTML:

@{
    ViewBag.Title = "Details";
    Layout = "~/Views/Shared/_Layout.cshtml";
    SchoolRegistration.Models.Student student = ViewBag.Student;
    List<SchoolRegistration.Models.Course> completedCourses = student.CompletedCourses;
    List<SchoolRegistration.Models.Course> pendingCourses = student.PendingCourses;
    List<SchoolRegistration.Models.Course> failedCourses = student.FailedCourses;
    List<int> yearRange = new List<int>();
    if (completedCourses != null) {
        foreach (SchoolRegistration.Models.Course course in completedCourses)
        {
            if (!yearRange.Contains(course.Year))
            {
                yearRange.Add(course.Year);
            }
        }
    }
    if (pendingCourses != null)
    {
        foreach (SchoolRegistration.Models.Course course in pendingCourses)
        {
            if (!yearRange.Contains(course.Year))
            {
                yearRange.Add(course.Year);
            }
        }
    }
    if (failedCourses != null)
    {
        foreach (SchoolRegistration.Models.Course course in failedCourses)
        {
            if (!yearRange.Contains(course.Year))
            {
                yearRange.Add(course.Year);
            }
        }
    }
    yearRange.Sort();
}

<h2>Details</h2>
@if (TempData["Success"] != null)
{
    if (TempData["Success"] == "nocourse")
    {
        <div style="background-color:aliceblue">
            <p><strong>Success:</strong><a href="#" id="createCourseLink">Create Course?</a></p>
        </div>
    }
    else
    {
        <div style="background-color:aliceblue">
            <p><strong>Success:</strong> @TempData["Success"].ToString()</p>
        </div>
    }
}

<h3>@student.FirstName @student.LastName - @student.StudentID</h3>

<h4>@student.GradeLevel - @student.ExpectedGraduation</h4>

<h3>Degree Audit</h3>
@using (Html.BeginForm("GetCourse", "Student", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
    @Html.AntiForgeryToken()
    <hr />
    @Html.ValidationSummary("", new { @class = "text-danger" })
    <p>Start Year: @Html.DropDownList("startYear", (IEnumerable<SelectListItem>)yearRange.ToList().Select(rr => new SelectListItem { Value = rr.ToString(), Text = rr.ToString() }).ToList(), "Select...") Start Semester: @Html.DropDownList("startSemester", EnumHelper.GetSelectList(typeof(SchoolRegistration.Models.Semesters)), "Select...")</p>
    <p>End Year: @Html.DropDownList("endYear", (IEnumerable<SelectListItem>)yearRange.ToList().Select(rr => new SelectListItem { Value = rr.ToString(), Text = rr.ToString() }).ToList(), "Select...") End Semester: @Html.DropDownList("endSemester", EnumHelper.GetSelectList(typeof(SchoolRegistration.Models.Semesters)), "Select...")</p>

    <input type="submit" value="Search" id="btnStudentFilter" />
}

<h3>Course Management</h3>
@using (Html.BeginForm("GetCourseForStudent", "Student", FormMethod.Post))
{
    @Html.AntiForgeryToken();
    <hr />
    <p><strong>Subject: </strong>@Html.DropDownList("cat", EnumHelper.GetSelectList(typeof(SchoolRegistration.Models.Categories)), "Select...")</p>
    <p><strong>CAT NO: </strong>@Html.TextBox("CourseId")</p>
    <p><strong>Section: </strong>@Html.TextBox("SectionId")</p>
    <p><strong>Year: </strong>@Html.TextBox("Year")</p>
    <p><strong>Semester: </strong>@Html.DropDownList("semester", EnumHelper.GetSelectList(typeof(SchoolRegistration.Models.Semesters)), "Select...")</p>
    <input type="submit" value="Search" />
}

@if (TempData["RetrievedCourses"] != null)
{
    List<SchoolRegistration.Models.Course> courses = (List<SchoolRegistration.Models.Course>)TempData["RetrievedCourses"];
    for (int i = 0; i < courses.Count; i++)
    {
        <p>@courses[i].Name - @courses[i].Category @courses[i].CourseId . @courses[i].SectionId - @courses[i].Credits Credits | @Html.ActionLink("Add to Student", "AddCourseForStudent", "Student", new { courseId = courses[i].id, studentId = student.id},null)</p>
    }
    <p>Course Search DIV Link</p>
}

AddCourseToStudent:

public ActionResult AddCourseForStudent(int courseId, int studentId)
    {
        using (var context = new StudentDbContext())
        {
            Student student = context.Students.Where(u => u.id == studentId).FirstOrDefault();
            Course course = context.Courses.Where(u => u.id == courseId).FirstOrDefault();

            List<Course> completed = student.CompletedCourses;
            List<Course> pending = student.PendingCourses;
            List<Course> failed = student.FailedCourses;

            if (completed == null)
                completed = new List<Course>();
            student.CompletedCourses = completed;
            if (pending == null)
                pending = new List<Course>();
            student.PendingCourses = pending;
            if (failed == null)
                failed = new List<Course>();
            student.FailedCourses = failed;
            pending.Add(course);
            try
            {
                context.Entry(student).State = System.Data.Entity.EntityState.Modified;
                context.SaveChanges();
                ViewBag.Student = student;
            }
            catch (DbEntityValidationException e)
            {
                foreach (var eve in e.EntityValidationErrors)
                {
                    Debug.WriteLine("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:",
                        eve.Entry.Entity.GetType().Name, eve.Entry.State);
                    foreach (var ve in eve.ValidationErrors)
                    {
                        Debug.WriteLine("- Property: \"{0}\", Error: \"{1}\"",
                            ve.PropertyName, ve.ErrorMessage);
                    }
                }
                throw;
            }
        }
        TempData["Success"] = "Courses added to student.";

        return Redirect(Request.UrlReferrer.ToString());
    }

Models:

public class InstructorViewModels
{
}

public enum GradeLevels
{
    Freshman,
    Sophmore,
    Junior,
    Senior,
    Graduate
}

public enum Categories
{
    CSCI
}

public enum Semesters
{
    FA,
    SP,
    S1,
    S2,
    S3,
    IN
}

public enum CourseTypes
{
    Humanities
}

public class Student
{
    public int id { get; set; }
    [Required]
    public int StudentID { get; set; }
    [Required]
    public string FirstName { get; set; }
    [Required]
    public string LastName { get; set; }
    public string MiddleName { get; set; }
    [Required]
    public GradeLevels GradeLevel { get; set; }
    [Required]
    public List<Course> CompletedCourses { get; set; }
    [Required]
    public List<Course> PendingCourses { get; set; }
    [Required]
    public List<Course> FailedCourses { get; set; }
    [Required]
    public int ExpectedGraduation { get; set; }
}

public class Course
{
    public int id { get; set; }
    public int CourseId { get; set; }
    [Required]
    public string Name { get; set; }
    [Required]
    public int SectionId { get; set; }
    [Required]
    public Categories Category { get; set; }
    [Required]
    public int Credits { get; set; }
    [Required]
    public Semesters Semester { get; set; }
    [Required]
    public int Year { get; set; }
    [Required]
    public string InstructorId { get; set; }
    public CourseTypes CourseType { get; set; }
}

Database:

enter image description here

enter image description here

Kyle
  • 2,339
  • 10
  • 33
  • 67
  • 1
    You might need to include the PendingCourses when you load: `ViewBag.Student = context.Students.Where(u => u.id == id).Include(i => i.PendingCourses).FirstOrDefault();` – Brendan Green May 06 '15 at 04:58
  • @BrendanGreen I dont think so as he is just ViewBag.Student = student; after save. So its the same object and therefore contains the same data. – rism May 06 '15 at 05:00
  • @Kyle We really need to see how you have set up your model as there should be an associative table such as StudentCourses. – rism May 06 '15 at 05:05
  • I noticed you are doing a Redirect at the end. This combined with ViewBag might be a problem: http://stackoverflow.com/a/14497811/1373170 – Pablo Romeo May 06 '15 at 05:06
  • @rism Just to be clear you want me to post the models for both the courses and students? – Kyle May 06 '15 at 05:09
  • @Kyle Yes IF it's code first. If its EDMX don't bother (maybe a screenshot). Do you have an associative table in your db? – rism May 06 '15 at 05:11
  • @PabloRomeo Interesting note, I will look at changing that. Although if that was the cause I would be interested as I also have ViewBag.Roles and ViewBag.Users which both work fine with redirects. – Kyle May 06 '15 at 05:12
  • @rism I am using code first migrations, I posted my model class. – Kyle May 06 '15 at 05:12
  • @Kyle There are a number of problems there. Starting with having no relationships between courses and student. Can you screen shot us your database tables to confirm. – rism May 06 '15 at 05:17
  • I suggest you read through this site starting at the top. Should take about an hour or so or you can quick step to the part about many to many relationships: http://www.entityframeworktutorial.net/code-first/configure-many-to-many-relationship-in-code-first.aspx – rism May 06 '15 at 05:20
  • @rism I thought by having the List would tie it to the courses? And yes my database screenshots are coming now. – Kyle May 06 '15 at 05:22
  • No that's just in C# code. EF doesn't know what to do with that. And the fact that you have 3 different List also requires a specific setup. See: InverseProperty on the link above. Plus you need to be using a public virtual ICollection so EF can intercept it etc not a concrete List. Basically, at this point, EF is just ignoring all your Student course lists as though they weren't there. – rism May 06 '15 at 05:27
  • @rism I think `List` might be fine. I agree that it looks like relationships are missing, and the collections would need to be `virtual`, though, for lazy loading. – jjj May 06 '15 at 05:30
  • Yep you're right my personal bias showing there. List implements ICollection, so all good. – rism May 06 '15 at 06:57

0 Answers0