I've got this all working, but it seems to be quite long-winded, and I thought I'd post here and see if I'm doing it wrong...
I have a M:M relationship between an Installer and a MasterInstance. The classes (code-first) look like:
public class MasterInstance
{
.. rest of fields here ..
public virtual ICollection<Installer> PermittedInstallers { get; set; }
}
public class Installer
{
.. rest of fields here ..
public virtual ICollection<MasterInstance> PermittedMasterInstances { get; set; }
}
I want to be able to edit these in my Installer view, using a multi-list box. So, I create a ViewModel for what I need:
public class InstallerViewModel
{
public Installer Installer { get; set; }
public List<MasterInstance> PermittedMasterInstances { get; set; }
public int[] SelectedMasterInstances { get; set; }
}
And then I place this in my view like so so that I can select the instances for my installer:
<div class="editor-field">
@Html.ListBoxFor(model => model.SelectedMasterInstances,(Model.PermittedMasterInstances).Select(option => new SelectListItem {
Text = option.Name,
Value = option.MasterInstanceId.ToString()
}))
</div>
In the controller, for the edit action, I need to set this view model up:
var installer = context.Installers.Include(i => i.PermittedMasterInstances).Single(x => x.InstallerId == installerId);
InstallerViewModel model = new InstallerViewModel
{
Installer = installer,
PermittedMasterInstances = context.MasterInstances.ToList(),
SelectedMasterInstances = installer.PermittedMasterInstances.Select(i => i.MasterInstanceId).ToArray()
};
return View(model);
Finally, on the post of the edit, I need to delete any relationships that are no longer there and add the new ones:
// Grab the model from the viewmodel and attach to the context
var installer = installerModel.Installer;
context.Installers.Attach(installer);
// Load the related records (dont know why Lazy Loading wouldn't kick in here)
context.Entry(installer).Collection(i => i.PermittedMasterInstances).Load();
// Iterate and delete existing relationships
var instancesToDelete = installer.PermittedMasterInstances.Where(mi => !installerModel.SelectedMasterInstances.Contains(i.MasterInstanceId)).ToList();
instancesToDelete.ForEach(mi => installer.PermittedMasterInstances.Remove(mi));
// Now loop through an int[] and add those new relations, WITHOUT the pain of fetching them from the DB
foreach (var permittedMasterInstanceId in installerModel.SelectedMasterInstances)
{
if (!installer.PermittedMasterInstances.Any(pmi => pmi.MasterInstanceId == permittedMasterInstanceId))
{
var masterInstance = new MasterInstance {MasterInstanceId = permittedMasterInstanceId};
context.MasterInstances.Attach(masterInstance);
installer.PermittedMasterInstances.Add(masterInstance);
}
}
// We're done - save and finish.
context.Entry<Installer>(installer).State = EntityState.Modified;
context.SaveChanges();
So this works... But, it seemed like a lot of effort, is this the right/best way to achieve it?