4

如果一个实体有多个关系并且我尝试在它们上同时插入数据,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 遵循代码优先模式创建数据库。

更新

正如@Chris 指出的那样,问题在于 EF 认为 Shoe 和 Watch 实体是相同的。这是由实施不当的重写 Equals 引起的。这实际上是问题的根源:

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

如果两个不同的实体类型(如WatchShoe)具有相同的 Id,则 EF 将它们视为相等。

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

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

2 回答 2

1

不确定为什么会引发该特定错误,但 EF 似乎由于您的 Entity.Id 定义而感到困惑。如果您将 PK 字段移至 Shoe 和 Watch 类定义,它就可以工作。此外,如果在通过集合将 watch 和 shoe 对象添加到 Person 对象之前,将它们添加到它们各自的 DbContext 集中,它也可以工作。无论如何,以一种或另一种方式稍微更明确地解决问题。

于 2013-08-12T13:38:53.417 回答
1

我不确定实体框架的内部结构(尽管我可能会尝试看看这个),但我认为你的问题可能是两件事的结合:

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

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

如果您满足以下任一条件,您的代码将运行:

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

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

Watch或 为或定义不同的 Guid 值Shoe

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

如果您不执行其中任何一个,并且按照第三个示例进行操作,则可以按照调试器进行操作,并发现您将到达一个用andEquals调用的点,结果为真-我认为这就是重点Entity Framework 在哪个位置抛出您的异常。WatchShoe

希望对 EF 内部有更多了解的人能够指出为什么会这样。

于 2013-08-12T13:44:19.037 回答