Reputation: 71
Basically I'm trying to add a new entity (Person) with its children (Addresses); and both of these entities do have many to many relationships. I'm using Entity Framework 6.1.3.
Attaching an entity of type 'Feature' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.
But I'm getting the following error even though I think that I'm using the proper Attach method to indicate that both Hobbies and Features already exist in the database.
It seems like if I try to attach only one featureSet instead of 2, then it works fine. But the problem seems to be when the same Feature is selected for 2 addresses. I've even tried to attach only the distinct feature, but then a different error comes. Then the code tries to ADD a NEW feature which I don't want.
Here is my code that I'm working with:
public class Person
{
public int PersonId { get; set; }
public string Personname { get; set }
public ICollection<Hobby> Hobbies { get; set; } // Many to Many with Hobbies
public ICollection<Address> Addresses { get; set; } // One to Many (many side)
}
// Many to Many: Represented in database as PersonHobby (eg. Reading, Writing; User could select multiple hobbies of a person)
public class Hobby
{
public int HobbyId { get; set; }
public string Hobbyname { get; set; }
public ICollection<Person> People { get; set; } // Many-To-Many with Person
}
public class Address
{
public int AddressId { get; set; }
public int PersonId { get; set; }
public string Line1 { get; set; }
public string City { get; set; }
public string State { get; set; }
public Person Person { get; set; }
public ICollection<Feature> Features { get; set; }
}
// Many to Many: Represented in database as AddressFeature (e.g Air Conditioning, Central Heating; User could select multiple features of a single address)
public class Feature
{
public int FeatureId { get; set; }
public string Featurename { get; set; }
public ICollection<Address> Addresses { get; set; } // Many-To-Many with Addresses
}
Now, I want to add a NEW person with multiple addresses in the same shot.
public static Person GetFakePerson()
{
Person person = new Person();
person.PersonName = "Peter";
person.PersonEmail = "[email protected]";
person.Hobbies = new List<Hobby>();
person.Hobbies.Add(new Hobby { HobbyId = 1 });
person.Hobbies.Add(new Hobby { HobbyId = 2 });
person.Addresses = new List<Address>();
// In real world, the user in GUI will be selecting the following features as the checkboxes within the Address object.
List<Feature> featureSet_1 = new List<Feature>();
featureSet_1.Add(new Feature { FeatureId = 1 });
featureSet_1.Add(new Feature { FeatureId = 2 });
person.Addresses.Add(new Address { Line1 = "123 St", City = "Fishers", State = "IN", Features = featureSet_1 });
List<Feature> featureSet_2 = new List<Feature>();
featureSet_2.Add(new Feature { FeatureId = 1 });
featureSet_2.Add(new Feature { FeatureId = 2 });
person.Addresses.Add(new Address { Line1 = "987 Avenue", City = "Carmel", State = "IN", Features = featureSet_2 });
return person;
}
// PersonService has this method
public int AddPerson(Person person)
{
try
{
using (MyDbContext dbContext = new MyDbContext())
{
person.Addresses.ToList().ForEach(y => y.Features.ToList().ForEach(x => dbContext.Features.Attach(x)));
person.Hobbies.ToList().ForEach(x => dbContext.Hobbies.Attach(x)); // This seems to be working
dbContext.People.Add(person);
dbContext.SaveChanges();
return person.PersonId;
}
}
catch(Exception exp)
{
// Catch exception here
}
}
This is how I'm calling the above mentioned function:
static void Main(string[] args)
{
PersonService _personService = new PersonService(); // This class holds the AddPerson()
Person samplePerson = GetFakePerson();
int peronId = _personService.AddPerson(samplePerson);
}
Upvotes: 1
Views: 93
Reputation: 109079
You're doing nearly everything right. The only mistake you make is in
List<Feature> featureSet_1 = new List<Feature>();
featureSet_1.Add(new Feature { FeatureId = 1 });
featureSet_1.Add(new Feature { FeatureId = 2 });
...
List<Feature> featureSet_2 = new List<Feature>();
featureSet_2.Add(new Feature { FeatureId = 1 });
featureSet_2.Add(new Feature { FeatureId = 2 });
...
featureSet_1
and featureSet_2
are two lists, together containing four Feature
instances. All EF knows, is that these four instances are attached to the context. At the third instance, the first in featureSet_2
, it complains that this primary key value is already "taken".
You have to assign the same two Feature
instances to both Address
es:
var f1 = new Feature { FeatureId = 1 };
var f2 = new Feature { FeatureId = 2 };
List<Feature> featureSet_1 = new List<Feature> { f1, f2 };
...
List<Feature> featureSet_2 = new List<Feature> { f1, f2 };
...
(Maybe EF is even happy if you assign featureSet_1
twice, to both addresses).
Upvotes: 1