reZach
reZach

Reputation: 9429

MVC An entity object cannot be referenced by multiple instances of IEntityChangeTracker

I've got a model that represents a joint table (with payload) in my database:

public class UserHasCar
{
    // Foreign keys
    [Key, Column(Order = 0)]
    public string ApplicationUserId { get; set; }

    [Key, Column(Order = 1)]
    public int CarId { get; set; }

    // Navigation properties
    [Required]
    public virtual ApplicationUser ApplicationUser { get; set; }

    [Required]
    public virtual Car Car{ get; set; }

    // Additional fields
    public int YearsRidden { get; set; }
}

public class Car
{
    public int ID { get; set; }
    public string Name { get; set; }

    public virtual ICollection<UserHasCar> UserHasCars { get; set; } 
}

public class ApplicationUser : IdentityUser
{
    public int BirthYear{ get; set; }

    public virtual ICollection<UserHasCar> UserHasCars { get; set; }
}

I have a form that includes multiple select boxes, and upon submitting I want to clear out all records related to that user who submitted the form in the UserHasCar table and replace them with the new updated information. I'm getting a An entity object cannot be referenced by multiple instances of IEntityChangeTracker. because I am doing something wrong, but I don't see where I am using more than one context. This code happens in my controller:

public ApplicationUser GetCurrentUser()
{
    return UserManager.FindById(User.Identity.GetUserId());
}

public string GetUserId()
{
    string id = User.Identity.GetUserId();

    var user = UserManager.FindById(id);

    return user.Id;
}

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ManageCars(FormCollection form)
{
    string id = GetUserId();

    // Remove cars records with my id from database
    var queryCars = (from m in db.UserHasCars where m.ApplicationUserId == id select m).ToList();

    foreach (var record in queryCars )
    {
        // Could the problem be here?
        db.UserHasCars.Remove(record)
    }

    // Add user-submitted cars to the database

    string carval = form["Cars[0]"];
    Car car = (from m in db.Cars where m.Name == carval select m).First();
    int carid = car.ID;

    // I get the abovementioned title error here
    db.UserHasCars.Add(
        new UserHasCar()
        {
            ApplicationUser = GetCurrentUser(),
            ApplicationUserId = id,
            Car = car,
            CarId = carid,
            YearsRidden = 0
        }
    );

    db.SaveChanges();

}

I've seen many SO posts, but can't seem the problem as why my code doesn't want to save the new database entries.


EDIT

The solution was to remove the call to get the user and replace it with a query. Why? I was making database conflict errors by having both types of calls (database and DataManager calls in the same controller action). I ended up using a modified GetUser() function instead of GetCurrentUser()

Code:

    public ApplicationUser GetUser()
    {
        // As opposed to:
        // UserManager.FindById(User.Identity.GetUserId())
        // We make a database call to grab our user instead
        // So we don't get database context conflicts by using UserManager
        string id = GetUserId();

        return db.Users.Where(m => m.Id == id).First();
    }

    public string GetUserId()
    {
        return User.Identity.GetUserId();
    }

    // snip
    // in ManageCars(FormCollection form)
    ApplicationUser user = GetUser();

    // snip
    var newRow = db.UserHasCars.Create();

    newRow.ApplicationUser = user;

    // snip

    db.UserHasCars.Add(newRow);

Upvotes: 1

Views: 1117

Answers (2)

Radu Porumb
Radu Porumb

Reputation: 785

Try removing this line:

ApplicationUser = GetCurrentUser(),

from your object instantiation when adding.

Entity populates this object automatically once you set the foreign key ApplicationUserId. If UserManager.FindById(User.Identity.GetUserId()) uses a different db context that's where your exception is coming from.

Also to save yourself further trouble down the line, you should always call db.SaveChanges() in between the two operations. If you're worried about the atomicity of the db operation, just wrap the whole thing in a Transaction.

And when adding new rows to a table, I usually prefer to use something like:

var newRow = db.SomeTable.Create();

newRow.SomeColumn1 = "something";
newRow.SomeColumn2 = 5;

db.SomeTable.Add(newRow);
db.SaveChanges();

Upvotes: 2

Kundan Singh Chouhan
Kundan Singh Chouhan

Reputation: 14282

In order to delete entries from UserHasCars you need to change their EntityState to Deleted.

Example:

foreach (var record in queryCars )
{
    db.ObjectStateManager.ChangeObjectState(record, EntityState.Deleted);
}

Hope this will fix your issue.

Upvotes: 0

Related Questions