Listbox for MVC 6 EF 7 Property not Populating

2020-04-20 08:20发布

问题:

I've been trying for a while now to get a list box to populate and I can't seem to figure it out. I've studied entity framework 7 documentation pretty extensively but I'm still new to it. There aren't a lot of tutorials out there for MVC6/EF7 yet so its been hard to know what the best practice is for associating one entity class with an instance of another. Please excuse the length of the question, I'm just trying to be thorough.

I'm using entity framework 7, asp.net 5 and MVC 6.

Steps To Reproduce Issue

  1. Create a new ASP.Net Web Application → Name of project: ListBox.Web → Name of solution ListBox
  2. Choose APS.NET 5 TemplatesWeb Application
  3. Create two classes in the Models folder

    Parent.cs

    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    
    namespace ListBox.Web.Models
    {
        public class Parent
        {
            public int ParentId { get; set; }
            [Required]
            public string Name { get; set; }
            public ICollection<Child> Children { get; set; }
        }
    }
    

    Child.cs

    using System.ComponentModel.DataAnnotations;
    
    namespace ListBox.Web.Models
    {
        public class Child
        {
            public int ChildId { get; set; }
            [Required]
            public string Name { get; set; }
            public int ParentId { get; set; }
            public Parent Parent { get; set; }
        }
    }
    
  4. Create controllers and views for each of the data classes using scaffolding

  5. Add links to the controllers in _Layout.cshtml

                <ul class="nav navbar-nav">
                    <li><a asp-controller="Home" asp-action="Index">Home</a></li>
                    <li><a asp-controller="Parents" asp-action="Index">Parents</a></li>
                    <li><a asp-controller="Children" asp-action="Index">Children</a></li>
                    <li><a asp-controller="Home" asp-action="About">About</a></li>
                    <li><a asp-controller="Home" asp-action="Contact">Contact</a></li>
                </ul>
    
  6. Create the database

    ListBox\src\ListBox.Web>dns ef migrations add Initial
    ListBox\src\ListBox.Web>dnx ef database update     
    
  7. Run the web application

  8. Add a couple parents.

  9. Attempt to add a child.

    • A drop box is shown for parents but there are no items in the drop box to select
    • The HTML for the list box is: <select class="form-control" data-val="true" data-val-required="The ParentId field is required." id="ParentId" name="ParentId"></select>

Controller Source Code

using System.Linq;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.Data.Entity;
using ListBox.Web.Models;

namespace ListBox.Web.Controllers
{
    public class ChildrenController : Controller
    {
        private ApplicationDbContext _context;

        public ChildrenController(ApplicationDbContext context)
        {
            _context = context;    
        }

        // GET: Children
        public IActionResult Index()
        {
            var applicationDbContext = _context.Child.Include(c => c.Parent);
            return View(applicationDbContext.ToList());
        }

        // GET: Children/Details/5
        public IActionResult Details(int? id)
        {
            if (id == null)
            {
                return HttpNotFound();
            }

            Child child = _context.Child.Single(m => m.ChildId == id);
            if (child == null)
            {
                return HttpNotFound();
            }

            return View(child);
        }

        // GET: Children/Create
        public IActionResult Create()
        {
            ViewData["ParentId"] = new SelectList(_context.Set<Parent>(), "ParentId", "Parent");
            return View();
        }

        // POST: Children/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Create(Child child)
        {
            if (ModelState.IsValid)
            {
                _context.Child.Add(child);
                _context.SaveChanges();
                return RedirectToAction("Index");
            }
            ViewData["ParentId"] = new SelectList(_context.Set<Parent>(), "ParentId", "Parent", child.ParentId);
            return View(child);
        }

        // GET: Children/Edit/5
        public IActionResult Edit(int? id)
        {
            if (id == null)
            {
                return HttpNotFound();
            }

            Child child = _context.Child.Single(m => m.ChildId == id);
            if (child == null)
            {
                return HttpNotFound();
            }
            ViewData["ParentId"] = new SelectList(_context.Set<Parent>(), "ParentId", "Parent", child.ParentId);
            return View(child);
        }

        // POST: Children/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Edit(Child child)
        {
            if (ModelState.IsValid)
            {
                _context.Update(child);
                _context.SaveChanges();
                return RedirectToAction("Index");
            }
            ViewData["ParentId"] = new SelectList(_context.Set<Parent>(), "ParentId", "Parent", child.ParentId);
            return View(child);
        }

        // GET: Children/Delete/5
        [ActionName("Delete")]
        public IActionResult Delete(int? id)
        {
            if (id == null)
            {
                return HttpNotFound();
            }

            Child child = _context.Child.Single(m => m.ChildId == id);
            if (child == null)
            {
                return HttpNotFound();
            }

            return View(child);
        }

        // POST: Children/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public IActionResult DeleteConfirmed(int id)
        {
            Child child = _context.Child.Single(m => m.ChildId == id);
            _context.Child.Remove(child);
            _context.SaveChanges();
            return RedirectToAction("Index");
        }
    }
}

Child Create.cshtml

@model ListBox.Web.Models.Child

@{
    ViewData["Title"] = "Create";
}

<h2>Create</h2>

<form asp-action="Create">
    <div class="form-horizontal">
        <h4>Child</h4>
        <hr />
        <div asp-validation-summary="ValidationSummary.ModelOnly" class="text-danger"></div>
        <div class="form-group">
            <label asp-for="Name" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="Name" class="form-control" />
                <span asp-validation-for="Name" class="text-danger" />
            </div>
        </div>
        <div class="form-group">
            <label asp-for="ParentId" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <select asp-for="ParentId" class ="form-control"></select>
            </div>
        </div>
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
</form>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
    <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
}

回答1:

Change Create() method in ChildrenController, change

    public IActionResult Create()
    {
        ViewData["ParentId"] = new SelectList(_context.Set<Parent>(), "ParentId", "Parent");
        return View();
    }

to

    public IActionResult Create()
    {
        ViewData["ParentId"] = new SelectList(_context.Set<Parent>(), "ParentId", "Name");
        return View();
    }

In Create.cshtml, change

<select asp-for="ParentId" class="form-control"></select>

to

@Html.DropDownList("ParentId", null, htmlAttributes: new { @class = "form-control" })


回答2:

The generated code is incorrect, it is a bug https://github.com/aspnet/Scaffolding/issues/149

One solution using "tag helpers" is:

Controller

...
ViewData["Parents"] = new SelectList(_context.Set<Parent>(), "ParentId", "Name", child.ParentId);
...

View

@{
    var parents = (IEnumerable<SelectListItem>)ViewData["Parents"];
}
...
<select asp-for="ParentId" asp-items="parents" class ="form-control">
     <option disabled selected>--- SELECT ---</option>
</select>
...


回答3:

Here's how to do it when there is only one type of object which is nested within another object of the same type.

Object:

public class Fleet
{

    public int Id { get; set; }
    public Fleet ParentFleet { get; set; }
    public int? ParentFleetId { get; set; }

    public string Name { get; set; }

    [InverseProperty("ParentFleet")]
    public virtual List<Fleet> Children { get; set; }

    public List<UserFleet> UserFleets { get; set; }
}

Controller:

ViewData["ParentFleetId"] = new SelectList(_context.Set<Fleet>(), "Id", "Name");
return View();

View:

<form asp-action="Create">
    <div class="form-horizontal">
        <h4>Fleet</h4>
        <hr />
        <div asp-validation-summary="ModelOnly" class="text-danger"></div>
        <div class="form-group">
            <label asp-for="Name" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="Name" class="form-control" />
                <span asp-validation-for="Name" class="text-danger" />
            </div>
        </div>
        <div class="form-group">
            <label asp-for="ParentFleet" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <select asp-for="ParentFleetId" asp-items="ViewBag.ParentFleetId" class="form-control">
                    <option value=""></option>
                </select>
            </div>
        </div>
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
</form>