Allow user to edit list items in MVC?

2019-09-21 05:08发布

问题:

I'm using Entity Framework Core to build a simple web app. For this app, I've created a model called Company that includes basic business info + a list of contacts (sales reps).

Here's my model:

public class Company
{
    [Key]
    public int ID { get; set; }
    public string Name { get; set; }
    public string Promo { get; set; } 
    public virtual List<Contact> Contacts { get; set; }       
}

public class Contact
{
    [Key]
    public int ContactID { get; set; }
    [ForeignKey("Company")]
    public int CompanyID { get; set; }
    public virtual Company Company { get; set; }
    public string ContactName { get; set; }
    public string ContactNumber { get; set; }
}

Here's the controller's index() method:

// GET: Companies
    public async Task<IActionResult> Index()
    {
        List<Company> viewModelData = await _context.Companies
            .Include(c => c.Contacts)
            .ToListAsync();
        return View(viewModelData);
    }

Edit method:

 // GET: Companies/Edit/5
        public async Task<IActionResult> Edit(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var company = await _context.Companies
                .Include(v => v.Contacts)
                .FirstOrDefaultAsync(m => m.ID == id);
            if (company == null)
            {
                return NotFound();
            }
            return View(company);
        }

        // POST: Companies/Edit/5

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit(int? id, [Bind("ID,Name,Promo,Contacts")] Company company)
        {
            if (id == null)
            {
                return NotFound();
            }

            var companyToUpdate = await _context.Companies
                .Include(v => v.Contacts)
                .FirstOrDefaultAsync(m => m.ID == id);
            if (await TryUpdateModelAsync<Company>(
                companyToUpdate,
                "",
                i => i.Name, i => i.Promo, i => i.Contacts
                )) { 
                try
                {
                    await _context.SaveChangesAsync();
                }
                catch (DbUpdateException /* ex */)
                {
                    //Log the error (uncomment ex variable name and write a log.)
                    ModelState.AddModelError("", "Unable to save changes. " +
                        "Try again, and if the problem persists, " +
                        "see your system administrator.");
                }
            return RedirectToAction("Index");
        }
         return View(companyToUpdate);
    }

This is not correct since the code only allows me to edit Company info. How do I modify the code so that I can edit both Company & its contacts on the same edit view?

回答1:

If you're purely looking to update values, then you can explicitly update them like so. A View Model is also recommended but this comes down to good vs bad practice. This omits the exception handling and serves only as an example of how to map these values, you'll have to modify the remainder of your controller to work directly with the CompanyEditViewModel

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int? id, [Bind("ID,Name,Promo,Contacts")] CompanyEditViewModel company)
{
    if (!ModelState.IsValid)
        return RedirectToAction("Index");

    var companyToUpdate = await _context.Companies
        .Include(v => v.Contacts)
        .FirstOrDefaultAsync(m => m.ID == id);

    // Assign the new values
    companyToUpdate.Name = company.Name;
    companyToUpdate.Promo = company.Promo;
    companyToUpdate.Contacts = company.Contacts?.ToList();

    // Update and save
    _context.Companies.Update(companyToUpdate);
    await _context.SaveChangesAsync();

    return View(companyToUpdate);
}

public class Company
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Promo { get; set; } // Yes or No field
    public List<Contact> Contacts { get; set; }

    public class Contact
    {
        [Key]
        public int ContactID { get; set; }

        public int CompanyID { get; set; }
        public string ContactName { get; set; }
        public string ContactNumber { get; set; }
    }
}

// The View Model contains the Company details which were modified
// The first Edit method will have to be updated to bind this View Model to the view
public class CompanyEditViewModel
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Promo { get; set; }
    public IList<Company.Contact> Contacts { get; set; }
}