Search code examples
c#entity-framework-coreasp.net-core-3.1scaffolding

EF Core, one-to-many relationship - how to insert records in both tables with a single view?


I have two models set up as follows:

public class NewCourseCategoryForm
{
    public int NewCourseCategoryFormID { get; set; }            
    
    public virtual ICollection<ProposedCourse> ProposedCourse { get; set; }
}

And then:

public class ProposedCourse
{
    public int ProposedCourseID { get; set; }           
    public string ProposedCourseName { get; set; }
    public string ProposedCourseDescription { get; set; }
    
    public NewCourseCategoryForm  { get; set; }
}

In my DbContext class definition, I have the following code which defines the relationships between the two aforementioned tables:

modelBuilder.Entity<NewCourseCategoryForm>(entity =>
{
    entity.HasKey(e => NewCourseCategoryFormID                  
    entity.Property(e => e.NewCourseCategoryFormID).HasColumnName("NewCourseCategoryFormID");

    entity.HasMany(d => d.ProposedCourse)
          .WithOne(p => p.NewCourseCategoryForm);

    modelBuilder.Entity<ProposedCourse>(entity =>
    {
        entity.HasKey(e => e.ProposedCourseID);    
        entity.Property(e => e.ProposedCourseName).HasColumnName("ProposedCourseName");
        entity.Property(e => e.ProposedCourseDescription).HasColumnName("ProposedCourseDescription");                   
    });

I want to create the CRUD pages for the NewCourseCategoryForm page. On the "Create" page, instead of seeing ProposedCourseID, I want to see the textboxes related to the ProposedCourse model for ProposedCourseName and ProposedCourseDescription. Whenever the user creates a new NewCourseCategoryForm, I want for records to be entered on to both tables like this:

+----------------------------+------------------+
| NewCourseCategoryFormID    | ProposedCourseID |
+----------------------------+------------------+
| 1                          | 123              |
+----------------------------+------------------+

+------------------+--------------------+---------------------------+
| ProposedCourseID | ProposedCourseName | ProposedCourseDescription |
+------------------+--------------------+---------------------------+
| 123              | Algebra 101        | Beginner algebra          |
+------------------+--------------------+---------------------------+

I'm feel certain that Entity Framework can handle something like this, but I have no idea where to begin looking. How can I make this happen? I even need the user to be able to enter a dynamic number of "Proposed Courses". So how can I accomplish that?


Solution

  • This should do the trick:

    CourseViewModels.cs:

    using System.Collections.Generic;
    
    namespace Test.Models
    {
        public class NewCourseCategoryForm
        {
            public int NewCourseCategoryFormID { get; set; }            
            
            public virtual ICollection<ProposedCourse> ProposedCourses { get; set; }
        }
    
        public class ProposedCourse
        {
            public int ProposedCourseID { get; set; }           
            public string ProposedCourseName { get; set; }
            public string ProposedCourseDescription { get; set; }
            
            public NewCourseCategoryForm Form { get; set; }
        }
    }
    

    CoursesController.cs:

    using System.Linq;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Logging;
    using Test.Models;
    
    namespace Test.Controllers
    {
        public class CoursesController : Controller
        {
            private readonly ILogger<CoursesController> _logger;
    
            public CoursesController(ILogger<CoursesController> logger)
            {
                _logger = logger;
            }
    
            [HttpGet]
            public IActionResult Create()
            {
                return View();
            }
    
            [HttpPost]
            public IActionResult Create(NewCourseCategoryForm form)
            {
                var newForm = new NewCourseCategoryForm
                {
                    ProposedCourses = form.ProposedCourses.Select(s => new ProposedCourse
                    {
                        ProposedCourseName = s.ProposedCourseName,
                        ProposedCourseDescription = s.ProposedCourseDescription
                    }).ToList()
                };
    
                // _context.NewCourseCategoryForms.Add(newForm);
                // _context.SaveChanges();
    
                return RedirectToAction("Create");
            }
        }
    }
    

    Create.cshtml:

    @model NewCourseCategoryForm
    @{
        ViewData["Title"] = "Home Page";
    }
    
    <form method="post">
        <div id="courses" class="row">
            <div class="col-md-6">
                <div class="form-group">
                    <label>Course name</label>
                    <input type="text" class="form-control" name="ProposedCourses[0].ProposedCourseName" placeholder="Name...">
                </div>
            </div>
            <div class="col-md-6">
                <div class="form-group">
                    <label>Course description</label>
                    <input type="text" class="form-control" name="ProposedCourses[0].ProposedCourseDescription" placeholder="Description...">
                </div>
            </div>
        </div>
    
        <button class="btn btn-primary" type="button" onclick="create()">New...</button>
        <button class="btn btn-secondary" type="submit">Create</button>
    </form>
    
    @section Scripts
    {
        <script type="text/javascript">
            var counter = 1;
    
            const create = () => {
                $("#courses").append(`
                    <div class="col-md-6">
                        <div class="form-group">
                            <label>Course name</label>
                            <input type="text" class="form-control" name="ProposedCourses[` + counter + `].ProposedCourseName" placeholder="Name...">
                        </div>
                    </div>
                    <div class="col-md-6">
                        <div class="form-group">
                            <label>Course description</label>
                            <input type="text" class="form-control" name="ProposedCourses[` + counter + `].ProposedCourseDescription" placeholder="Description...">
                        </div>
                    </div>
                `);
    
                counter++;
            }
        </script>
    }
    

    Courses page

    POSTed dataset