If an object has several relationships, and I try to insert data on them at the same time, EF raises an InvalidCastException.
As an example, imagine these domain classes:
public class Person : Entity<Guid> { public string Name { get; set; } public ICollection<Watch> Watches { get; set; } public ICollection<Shoe> Shoes { get; set; } } public class Shoe : Entity<Guid> { public string Brand { get; set; } } public class Watch : Entity<Guid> { public string Brand { get; set; } }
Use case # 1 (works great):
using (var context = new MultipleRelationshipsContext()) { var watches = new List<Watch>() { new Watch { Brand = "Rolex" } }; context.Set<Person>().Add( new Person { Name = "Warren Buffett", Watches = watches } ); }
Use case # 2 (works great):
using (var context = new MultipleRelationshipsContext()) { var shoes = new List<Shoe>() { new Shoe { Brand = "Cole Haan" } }; context.Set<Person>().Add( new Person { Name = "Barack Obama", Shoes = shoes } ); }
Use case # 3 (InvalidCastException):
using (var context = new MultipleRelationshipsContext()) { var watches = new List<Watch>() { new Watch { Brand = "Casio" } }; var shoes = new List<Shoe>() { new Shoe { Brand = "New Balance" } }; context.Set<Person>().Add( new Person { Name = "Steve Jobs", Watches = watches, Shoes = shoes } ); }
In the third case, an InvalidCastException is thrown, saying that EF cannot drop from ' EntityFrameworkMultipleRelationships.Entities.Watch to' EntityFrameworkMultipleRelationships.Entities.Shoe '.
I'm new to EF, but I think something is going wrong here.
I would add some hint to point out a possible solution!
PD: To test yourself as quickly as possible, download this VS2012 solution: https://dl.dropboxusercontent.com/u/22887057/EntityFrameworkMultipleRelationships.zip . Follow README.txt to create a database using a code template.
UPDATE
As @Chris noted, the problem was that EF considers the Shoe and Watch objects to be the same. This was caused by a poorly implemented excess of Equals. This is actually a source of problems:
public abstract class Entity<T> { [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] [Column("Id")] public T Id { get; set; } public override bool Equals(object obj) { Entity<T> entityOfT = obj as Entity<T>; if (entityOfT == null) return false; return object.Equals(this.Id, entityOfT.Id); } public override int GetHashCode() { return this.Id.GetHashCode(); } }
If two different types of entities (e.g. Watch and Shoe ) have the same identifier, EF considers them equal.
Adding a runtime type check to override Equals takes into account the type of the object and therefore solves this problem.
... return this.GetType() == entityOfT.GetType() && object.Equals(this.Id, entityOfT.Id); ...