I have 3 many-many tables: Users -< UserRoles >- Roles. I set up my model like this:
public class User
{
public int UserId {get; set;}
The key is that you need your collection properly rendering in the view. First off, add a Boolean property to the Role view data object so we have something to bind our check box to:
public class Role
{
public bool IsInRole { get; set; }
[HiddenInput(DisplayValue = false)]
public int RoleId { get; set; }
[HiddenInput(DisplayValue = true)]
public string RoleName { get; set; }
}
Notice I put some HiddenInput attribute on the properties (more on that later). Also you could pass your User object as shown above to the view - this already has the child collection of Roles. There are a few ways to render this collection in the view, but one of the easiest is:
<%: Html.EditorFor(m => m.Roles) %>
Now add an editor template for the Role object so the line above does what we want. Add Role.ascx to /Views/Shared/EditorTemplates folder. Roles.ascx can looking something like this:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%: Html.EditorFor(m => m.IsInRole) %>
<%: Html.EditorFor(m => m.RoleId) %>
<%: Html.EditorFor(m => m.RoleName) %>
You'll see when you do a view source that your html looks something like this:
RoleName1
RoleName2
This is key for model binding when your form is posted back. We used DisplayValue=true for the display name because we need the hidden input for post back model binding, but it's need to be read-only. for the roleId, that is a hidden input and no value is displayed to the user. See this post for more information on the HiddenInput.
When you post this back, MVC built-in model binder will ensure that your roles collection is constructed property and you'll see the check boxes state properly reflected in your model.