patterncsharpMinor
Efficient way to deal with maintaining Many:Many relationships in EF Code-First
Viewed 0 times
withefficientwayfirstmanycodemaintainingrelationshipsdeal
Problem
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:
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:
And then I place this in my view like so so that I can select the instances for my installer:
In the controller, for the edit action, I need to set this view model up:
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
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 PermittedInstallers { get; set; }
}
public class Installer
{
.. rest of fields here ..
public virtual ICollection 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 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:
@Html.ListBoxFor(model => model.SelectedMasterInstances,(Model.PermittedMasterInstances).Select(option => new SelectListItem {
Text = option.Name,
Value = option.MasterInstanceId.ToString()
}))
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
Solution
I know this is an old question, but I think the problem is timeless. Many to many associations (i.e. without junction class) in Entity Framework are always independent associations, so you can only establish or remove them by manipulating object collections, not primitive key values. Inefficiency is inherent to the implementation.
But it is not prohibited to have a second context that only contains junction tables.
You could create a context that contains the
Now, if necessary you can populate the updated many to many association through the main context.
But it is not prohibited to have a second context that only contains junction tables.
You could create a context that contains the
MasterInstanceInstaller junction table and use this to update the associations in the most efficient way you can get using EF:var installer = installerModel.Installer;
var junctions = context.MasterInstanceInstallers
.Where(x => x.InstallerId == installer.InstallerId)
.ToList();
// Delete deselected instances.
foreach(var mi in junctions
.Where(x => !installerModel.SelectedMasterInstances
.Contains(x.MasterInstanceId)))
{
context.MasterInstanceInstallers.Remove(mi);
}
// Add newly selected instances.
foreach(int instanceId in installerModel.SelectedMasterInstances
.Except(junctions.Select(j => j.MasterInstanceId)))
}
context.MasterInstanceInstallers.Add(new MasterInstanceInstaller
{
InstallerId = installer.InstallerId,
MasterInstanceId = instanceId
}
);
}
context.SaveChanges();Now, if necessary you can populate the updated many to many association through the main context.
Code Snippets
var installer = installerModel.Installer;
var junctions = context.MasterInstanceInstallers
.Where(x => x.InstallerId == installer.InstallerId)
.ToList();
// Delete deselected instances.
foreach(var mi in junctions
.Where(x => !installerModel.SelectedMasterInstances
.Contains(x.MasterInstanceId)))
{
context.MasterInstanceInstallers.Remove(mi);
}
// Add newly selected instances.
foreach(int instanceId in installerModel.SelectedMasterInstances
.Except(junctions.Select(j => j.MasterInstanceId)))
}
context.MasterInstanceInstallers.Add(new MasterInstanceInstaller
{
InstallerId = installer.InstallerId,
MasterInstanceId = instanceId
}
);
}
context.SaveChanges();Context
StackExchange Code Review Q#8537, answer score: 5
Revisions (0)
No revisions yet.