0

I am coding a MVC app that contains a couple of many-to-many associations. One of them is a custom Role <---> Account association. Basically I have a table full of pre-defined Roles from which the user can choose. I created a ViewModel which holds my entity model and a few collections that I use, one of them the Roles collection. I then populate my Create form with these values and resolve them again on [HttpPost] Create action.

Here is the relevant code:

ViewModel Class:

public class AccountsViewModel
{
    public Accounts Account { get; set; }

    public List<Roles> RolesList { get; set; }
}

Controller code:

public ActionResult Create()
{
    AccountsViewModel viewModel = new AccountsViewModel();
    viewModel.RolesList = rolesService.GetAllRoles();  
    return View(viewModel);  
}

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(AccountsViewModel viewModel)
{
    if (ModelState.IsValid)
    {
        foreach(Roles role in viewModel.RolesList)
        {
             if (role.IsSelected)
             {
                  Roles selectedRole = rolesService.GetRole(role.Id);
                  viewModel.Account.Roles.Add(selectedRole);
             }
         }        
         //Some more code here...        
         accountsService.AddAccount(viewModel.Account);
    }
}

Custom service class (accountsService)

public void AddAccount(Accounts newAccount)
{
    //AppDataContext is an instance of <Model>Container
    AppDataContext.AccountsSet.Add(newAccount);
    AppDataContext.SaveChanges();
}

And my Create View:

<div class="form-group">
    @Html.LabelFor(model => model.Account.Roles, htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @for (int i = 0; i < Model.RolesList.Count; i++)
        {
            @Html.HiddenFor(m => m.RolesList[i].Id)
            @Html.CheckBoxFor(m => m.RolesList[i].IsSelected)
            @Html.LabelFor(m => m.RolesList[i].IsSelected, Model.RolesList[i].Name)
            <br />
        }
    </div>
</div>

Now to my actual problem, each time I try to add a new Accounts object, I get an error "An entity object cannot be referenced by multiple instances of IEntityChangeTracker." I have already looked at a few posts found on the internet, but I can't really associate them with any possible errors I made in my code. So can anybody help me out here?

LeonidasFett
  • 3,052
  • 4
  • 46
  • 76

1 Answers1

0

The Problem is that the roles of the newAccount are still attached to the context of the roleService. An Entity can only be attached to one context at a time.

You can either call context.Entity(...).Detach() to detach the entity, or even better get the role with Change Tracking disabled and without proxy creation, as this will give you a huge speed boost to:

context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ProxyCreationEnabled = false;

Even better create a new context within a using block inside of rolesService.GetRole(...) that way your context won't get polluted (and slow) over time.

wertzui
  • 5,148
  • 3
  • 31
  • 51
  • so what you are trying to say is that if I change something in the "current" context, I need to either dispose and instantiate it again or use the same context for all my custom service classes? – LeonidasFett Jan 28 '16 at 15:34
  • just one concern though, if I dispose/instantiate the context multiple times during, let's say, an item edit action, won't it slow things down even more? – LeonidasFett Jan 28 '16 at 15:37
  • You have to either detach the Entity from contextA to add it to contextB or disable ProxyCreation. Context creation and dispose is a very fast operation compared to accessing something from the database. – wertzui Jan 29 '16 at 09:34