Entity Framework 4.1 Code-First and Inserting New One-to-Many Relationships
I am having trouble peristing a new object graph to the context with a one-to-many relationship. I am using the Entity Framework 4.1 release, and implementing a Code-First approach. I am using an existing SQL 2008 database and implemented a context derived from DbContext. I have two classes, Person and Address. A person can contain 0 or more Addresses, defined as such.
public class Person
{
public Person()
{
Addresses = new List<Address>();
}
public int PersonId { get; set; }
***Additional Primitive Properties***
public virtual ICollection<Address> Addresses { get; set; }
}
public class Address
{
public int AddressId { get; set; }
public int AddressTypeId { get; set; }
***Additional Primitive Properties***
public int P开发者_如何学JAVAersonId { get; set; }
public virtual Person Person { get; set; }
}
I am trying to create a new instance of Person with two addresses. However, when I add this structure to the context and save, only the first Address in the collection is persisted. The second has the Person navigation property set to null, and is not associated with the Person object, however, the first one in the list is associated.
var person = new Person();
var mailingAddress = new Address() { AddressTypeId = 1 };
person.Addresses.Add(mailingAddress);
var billingAddress = new Address() { AddressTypeId = 2 };
person.Addresses.Add(billingAddress);
context.People.Add(entity);
context.SaveChanges();
It does not throw an exception, but the second item in the Address collection is just not saved.
Does anybody have any good ideas on why only the first would be saved? Thank you.
After hours of troubleshooting/trial and error, I've solved my problem. My POCO classes are also used in a disconnected environment, where the objects are detached from the context, modified, and then re-attached.
In order to determine which navigation property collection items were affected, I overrode the Equals and GetHashCode methods in the Address class to determine equality. Apparently this affects the ability for EF 4.1 to insert a complete collection of navigation property objects???
Here are the original equality methods which caused the issue:
public override bool Equals(object obj)
{
Address address = obj as Address;
if (address == null) return false;
return address.AddressId == this.AddressId;
}
public override int GetHashCode()
{
return this.AddressId.GetHashCode();
}
In order to correct the problem, I created a custom equality comparer for the navigation object rather than including it directly in the address class.
public class AddressEqualityComparer : IEqualityComparer<Address>
{
public bool Equals(Address address1, Address address2)
{
if (address1.AddressId == address2.AddressId)
return true;
else
return false;
}
public int GetHashCode(Address address)
{
return address.AddressId.GetHashCode();
}
}
My context.People.Add method call worked as expected after I made this change.
If anyone knows why overriding the equality methods in the class causes EF 4.1 to only insert the first item in the collection, that would be great information.
As hinted at already, it's because the GetHashCode method is using the ID of all the siblings, which will be 0 at point of comparison by Entity Framework. Comment just that out and you will good to go.
I had the same exact issue and this piece let me to that. I didn't even bother to look at my EntityBase code...it's so old and hasn't changed in forever until now.
So a big thank you for your research!
Here is another way to attempt to add the code. Worth a shot. This code may not be exact, I typed freehand.
var person = new Person();
person.Addresses.Add(new Address()
{
AddressTypeId = 1
}),
new Address()
{
AddressTypeId = 2
});
context.People.Add(entity);
context.SaveChanges();
精彩评论