Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework Code First Many to Many creating duplicate rows

My issue turned out to be having two context's. I reworked my code a bit to only have one context and my issue went away.

I have a User which has a list of UserContact's which itself has a ContactOption. Its a fairly simple 1 to many, many to 1 with the UserContact table in the middle.

If I pull the user out of the db and create a new UserContact, but set the ContactOption to an existing item (which I've pulled out of the db), when I SaveChanges, entity framework creates a new ContactOption in the database that is essentially a duplicate of the one I added to the UserContact (with the exception that it gets a new id).

I've battled with this for several hours and can't figure this out. Any ideas?

I am using a Repository pattern for my database queries, but I have ensured they are sharing the same context.

I pull the user out of the database with this:

var user = _context.Users.Include("UserContacts.ContactOption")
              .Where(id => id == 1);

And contact options are pulled out with:

var co = _context.ContactOptions.FirstOrDefault(c => c.Id == id);

And I add the ContactOption to the UserContact like so:

var contactOption = _context.ContactOptions.FirstOrDefault(c => c.Id == someId);

var contact = new UserContact { ContactOption = contactOption  };
contact.Data = "someData";

user.UserContacts.Add(contact);

My model looks like this:

public class User
{
    [Key]
    public int Id { get; set; }

    public virtual ICollection<UserContact> UserContacts { get; set; }
}

public class UserContact
{
    [Key]
    public int Id { get; set; }

    [Required]
    public User User { get; set; }

    [Required]
    public ContactOption ContactOption { get; set; }
    public string Data { get; set; }
}

public class ContactOption
{
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
}
like image 367
Dan Morphis Avatar asked Nov 13 '10 19:11

Dan Morphis


2 Answers

I run your code and got the exact expected results: A new row in UserContacts table with the existing UserId and ContactOptionId, so I am not sure what's happing in there, but you can try to explicitly have FKs in UserContact object so that you'll have full control over how Code First inserts records for you. For that, you need to change UserContact as follows:

public class UserContact
{
    public int Id { get; set; }

    // By Convention these 2 properties will be picked up as the FKs: 
    public int UserId { get; set; }
    public int ContactOptionId { get; set; }

    [Required]
    public User User { get; set; }        
    [Required]
    public ContactOption ContactOption { get; set; }

    public string Data { get; set; }
}

And then you can change your code like this:

var contactOption = _context.ContactOptions.Find(someId);
var user = _context.Users.Find(1);

var contact = new UserContact 
{ 
    ContactOptionId = contactOption.Id,  
    UserId = user.Id,
    Data = "someData"
};

user.UserContacts.Add(contact);
context.SaveChanges();
like image 130
Morteza Manavi Avatar answered Oct 10 '22 02:10

Morteza Manavi


My issue turned out to be having two context's. I reworked my code a bit to only have one context and my issue went away. Thanks to Morteza Manavi for pointing me in the right direction.

like image 33
Dan Morphis Avatar answered Oct 10 '22 02:10

Dan Morphis