为什么EF Code First [InverseProperty]属性与[ForeignKey]属性一起使用时无效?

时间:2012-04-04 23:26:56

标签: entity-framework foreign-keys ef-code-first data-annotations entity-framework-4.3

使用:EF 4.3.1,Visual Studio 2010,SQL CE 4.0

我的理解是,在EF中使用DataAnnotation声明外键时,可以通过以下方式之一完成:

选项1 -

[ForeignKey("Player1Home")]
public long? HPlayer1Id { get; set; }

public virtual Player Player1Home { get; set; }

选项2 -

public long? HPlayer1Id { get; set; }

[ForeignKey("HPlayer1Id")]
public virtual Player Player1Home { get; set; }

问题:

当InverseProperty DataAnnotation与选项2一起使用时,除了HPlayer1Id之外,还会在数据库(Player1Home_Id)中生成额外的列。

[Table("Matches")]
public class Match
{
    [Key]
    public long Id { get; set; }

    //-- Option 1 - THIS WORKS GREAT --//
    public long? HPlayer1Id { get; set; }
    [ForeignKey("HPlayer1Id")]
    public virtual Player Player1Home { get; set; }

    //-- Option 2 - THIS DOES NOT WORK, it generates an extra column in the database --//
    [ForeignKey("Player1Home")]
    public long? HPlayer1Id { get; set; }
    public virtual Player Player1Home { get; set; }
}

[Table("Players")]
public class Player
{
    [Key]
    public long Id { get; set; }

    [InverseProperty("Player1Home")]
    public virtual ICollection<Match> MatchesAsHome1 { get; set; }
}

当然如果我将HPlayer1Id重命名为Player1HomeId,那么Option 2再次正常工作。但是,当“约定”无法自动确定匹配属性时,DataAnnotation的整个目的是允许显式命名。

删除Player类上的InverseProperty DataAnnotation似乎也解决了这个问题,但不幸的是我不能这样做,因为我的实际Match类中有四个玩家,因此我需要显式映射。

最后,我知道我可以使用选项1,但我更喜欢在Id字段上声明所有密钥(主要和外部)的一致性,而不是导航属性上的外键。从技术上讲,无论哪种方式都可行。

这只是4.3.1中的错误吗?在EF Code中首先?

或者是将ForeignKey和InverseProperty从两个不同的属性映射到不支持的常见第三个属性?

非常感谢任何信息!

更新:第二个错误?

第三个选项也应该起作用(如Slauma所建议的那样),但是在我第一次尝试向数据库添加实体时会引发NullReferenceException。数据库永远不会被创建,而上面的选项2没有这个问题。看来这适用于EF 4.1上的Slauma,但对于我来说不适用于EF 4.3.1。 (我在SQL CE 4.0中使用它)

[Table("Matches")]
public class Match
{
    [Key]
    public long Id { get; set; }
    [ForeignKey("Player1Home")]
    public long? HPlayer1Id { get; set; }
    [InverseProperty("MatchesAsHome1")]
    public virtual Player Player1Home { get; set; }
}

[Table("Players")]
public class Player
{
    [Key]
    public long Id { get; set; }
    public virtual ICollection<Match> MatchesAsHome1 { get; set; }
}

public class MyContext : DbContext
{
    public DbSet<Match> Matches { get; set; }
    public DbSet<Player> Players { get; set; }
}

用法:

try
{
    MyContext mc = new MyContext();
    //NullReferenceException gets thrown on the next call
    mc.Matches.Add(new Match());
    mc.SaveChanges();
}
catch (Exception ex)
{
    MessageBox.Show(ex.Message);
}

2 个答案:

答案 0 :(得分:3)

这已在EF5中修复,我确认它在EF6中仍能正常运行。

您可以查看有关此问题的调查说明 - https://entityframework.codeplex.com/workitem/138

答案 1 :(得分:2)

EF 4.1中的相同行为。

您没有提及将InverseProperty属性移至关系另一侧的选项:

[Table("Matches")]
public class Match
{
    [Key]
    public long Id { get; set; }

    [ForeignKey("Player1Home")]
    public long? HPlayer1Id { get; set; }

    [InverseProperty("MatchesAsHome1")]
    public virtual Player Player1Home { get; set; }
}

[Table("Players")]
public class Player
{
    [Key]
    public long Id { get; set; }

    public virtual ICollection<Match> MatchesAsHome1 { get; set; }
}

这对我有用,并没有创建额外的列。

您的选项2的行为看起来像是代码优先的错误。

修改

确认将版本从EF 4.1更改为EF 4.3.1会导致上面的模型NullReferenceException。数据库无法创建。