InvalidCastException: EF crashes when inserting an object with multiple relationships

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); ... 
+4
source share
2 answers

I'm not sure about the internals of the Entity Framework (although I could try and take care of this), but I think the problem is a possible combination of two things:

1) The way that the Entity Framework automatically generates sets that store your entities.

2) You have two children with the same Guid, which will return Equals(..) true, although they are of different types.

Your code will run if you:

Define either Shoe / Watch (or both), and add it / them to the appropriate set (s):

 context.Set<Shoe>().Add(aShoe); 

Or Define a different value for Guid for a Watch or Shoe

 Watch tWatch = new Watch { Brand = "Casio", Id = new System.Guid("00000000-0000-0000-0000-000000000001") }; 

If you do neither one and the other, and you follow your third example, you can follow the debugger and find that you will reach the point where Equals is called using Watch and Shoe , and the result is correct - I assume that the point at Which Entity Framework throws your exception.

Hopefully someone who knows a little about the internal functions of EF will be able to indicate why this is the case.

+1
source

Not sure why this particular error occurs, but EF seems to get confused due to your definition of Entity.Id. If you move the PK field to the definitions of the classes of shoes and watches, it will work. Also, if you add watch and shoe objects to their respective DbContext sets before adding them through collections to the Person object, it also works. In any case, being a little more explicit, one way or another solves the problem.

+1
source

Source: https://habr.com/ru/post/1496520/


All Articles