c# – InvalidCastException:插入具有多个关系的实体时EF失败

如果一个实体有多个关系,并且我尝试同时插入数据,则EF会抛出InvalidCastException.

举个例子,想象一下这些域类:

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; }
}

用例#1(完美工作):

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
        }
    );
}

用例#2(也完美地工作):

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
        }
    );
}

用例#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
        }
    );
}

在第三种情况下,抛出InvalidCastException,表示EF无法从’EntityFrameworkMultipleRelationships.Entities.Watch转换为’EntityFrameworkMultipleRelationships.Entities.Shoe’.

我是一名EF新手,但我觉得这里出了点问题.

我要感谢任何暗示可能的解决方案!

PD.:为了尽可能快地测试自己,请下载此VS2012解决方案:https://dl.dropboxusercontent.com/u/22887057/EntityFrameworkMultipleRelationships.zip.按照README.txt按照代码优先模式创建数据库.

UPDATE

正如@Chris指出的那样,问题在于EF认为Shoe和Watch实体是相同的.这是由于实施的覆盖等于错误造成的.这实际上是问题的根源:

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();
    }
}

如果两个不同的实体类型(如Watch和Shoe)具有相同的Id,则EF正在考虑它们等于.

添加运行时类型检查以覆盖Equals会考虑实体类型,因此可以解决此问题.

...
return this.GetType() == entityOfT.GetType() && object.Equals(this.Id, entityOfT.Id);
...

最佳答案 我不确定实体框架的内部结构(虽然我可能会尝试看一看),但我认为你的问题可能是两件事的组合:

1)实体框架自动生成存储实体的集合的方式.

2)你有两个具有相同Guid的子对象,它们将返回Equals(..)true,即使它们的类型不同.

如果您要么:

定义Shoe / Watch(或两者),并将它/它们添加到相应的集合中:

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

或者为手表或鞋子的Guid定义不同的值

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

如果你不做其中任何一个,并按照你的第三个例子,你可以通过调试器,发现你将达到一个用Watch and Shoe调用Equals的点,结果是真的 – 我假设这就是Entity Framework抛出异常的重点.

希望能够了解更多关于EF内部结构的人能够指出为什么会这样.

点赞