MVC 4 Edit Controller/ View Many to Many relation and checkboxes c# checkbox entity-framework many-to-many


I'm working with ASP.NET MVC 4 and Entity Framework and I was searching for some way to make many to many relation and checkboxes from my db for a Create/Edit controller and view, I have found the answer with @Slauma answer for Create in MVC 4 - Many-to-Many relation and checkboxes but, I'd really like to see how this extends to Edit and Delete functionality as well like some other partners in this solution. Could someone please show how I would populate the ClassificationSelectViewModel in the Edit controller method to get both the "checked" and "unchecked" values? this is a Matt Flowers question that will solve mine too.

5/23/2017 12:26:41 PM

Accepted Answer

The following is a continuation of this answer that describes Create GET and POST actions for a model with many-to-many relationship between entities Subscription and Company. Here is the procedure for the Edit actions how I would do it (except that I probably wouldn't put all the EF code into the controller actions but extract it into extension and service methods):

The CompanySelectViewModel remains unchanged:

public class CompanySelectViewModel
    public int CompanyId { get; set; }
    public string Name { get; set; }
    public bool IsSelected { get; set; }

The SubscriptionEditViewModel is the SubscriptionCreateViewModel plus the Subscription's key property:

public class SubscriptionEditViewModel
    public int Id { get; set; }
    public int Amount { get; set; }
    public IEnumerable<CompanySelectViewModel> Companies { get; set; }

The GET action could look like this:

public ActionResult Edit(int id)
    // Load the subscription with the requested id from the DB
    // together with its current related companies (only their Ids)
    var data = _context.Subscriptions
        .Where(s => s.SubscriptionId == id)
        .Select(s => new
            ViewModel = new SubscriptionEditViewModel
                Id = s.SubscriptionId
                Amount = s.Amount
            CompanyIds = s.Companies.Select(c => c.CompanyId)

    if (data == null)
        return HttpNotFound();

    // Load all companies from the DB
    data.ViewModel.Companies = _context.Companies
        .Select(c => new CompanySelectViewModel
            CompanyId = c.CompanyId,
            Name = c.Name

    // Set IsSelected flag: true (= checkbox checked) if the company
    // is already related with the subscription; false, if not
    foreach (var c in data.ViewModel.Companies)
        c.IsSelected = data.CompanyIds.Contains(c.CompanyId);

    return View(data.ViewModel);

The Edit view is the Create view plus a hidden field for the Subscription's key property Id:

@model SubscriptionEditViewModel

@using (Html.BeginForm()) {

    @Html.HiddenFor(model => model.Id)
    @Html.EditorFor(model => model.Amount)

    @Html.EditorFor(model => model.Companies)

    <input type="submit" value="Save changes" />
    @Html.ActionLink("Cancel", "Index")

The editor template to select a company remains unchanged:

@model CompanySelectViewModel

@Html.HiddenFor(model => model.CompanyId)
@Html.HiddenFor(model => model.Name)

@Html.LabelFor(model => model.IsSelected, Model.Name)
@Html.EditorFor(model => model.IsSelected)

And the POST action could be like this:

public ActionResult Edit(SubscriptionEditViewModel viewModel)
    if (ModelState.IsValid)
        var subscription = _context.Subscriptions.Include(s => s.Companies)
            .SingleOrDefault(s => s.SubscriptionId == viewModel.Id);

        if (subscription != null)
            // Update scalar properties like "Amount"
            subscription.Amount = viewModel.Amount;
            // or more generic for multiple scalar properties
            // _context.Entry(subscription).CurrentValues.SetValues(viewModel);
            // But this will work only if you use the same key property name
            // in ViewModel and entity

            foreach (var company in viewModel.Companies)
                if (company.IsSelected)
                    if (!subscription.Companies.Any(
                        c => c.CompanyId == company.CompanyId))
                        // if company is selected but not yet
                        // related in DB, add relationship
                        var addedCompany = new Company
                            { CompanyId = company.CompanyId };
                    var removedCompany = subscription.Companies
                       .SingleOrDefault(c => c.CompanyId == company.CompanyId);
                    if (removedCompany != null)
                        // if company is not selected but currently
                        // related in DB, remove relationship


        return RedirectToAction("Index");

    return View(viewModel);

The Delete actions are less difficult. In the GET action you could load a few subscription properties to display on the delete confirmation view:

public ActionResult Delete(int id)
    // Load subscription with given id from DB
    // and populate a `SubscriptionDeleteViewModel`.
    // It does not need to contain the related companies

    return View(viewModel);

And in the POST action you load the entity and delete it then. There is no need to include the companies because in a many-to-many relationship (usually) cascading delete on the link table is enabled so that the database will take care of deleting the link entries together with the parent Subscription:

[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirm(int id)
    var subscription = _context.Subscriptions.Find(id);
    if (subscription != null)

    return RedirectToAction("Index");
5/23/2017 10:34:04 AM

Related Questions


Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow