Why doesn't the EF Code First [InverseProperty] attribute work when used with the [ForeignKey] attribute?

Usage: EF 4.3.1, Visual Studio 2010, SQL CE 4.0

I understand that when declaring foreign keys with DataAnnotation in EF, this can be done in one of the following ways:

Option 1 -

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

public virtual Player Player1Home { get; set; }

      

Option 2 -

public long? HPlayer1Id { get; set; }

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

      

Problem:

When InverseProperty DataAnnotation is used with Option 2, an additional additional column is created in the database (Player1Home_Id) in addition to HPlayer1Id.
[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; }
}

      

Of course, if I rename HPlayer1Id to Player1HomeId, then Option 2 works correctly again. But the whole purpose of DataAnnotation is to allow explicit naming when "Conventions" cannot automatically determine the matching property.

Removing the InverseProperty DataAnnotation in the Player class also seems to fix the problem, but unfortunately I cannot do that because my actual Match class has four players in it and so I need explicit mappings.

Finally, I know I can just use Option 1, but I prefer the consistency of declaring all my keys (primary and foreign) in the "Identifier" fields, rather than the "Foreign Keys" in the Navigation Properties. And technically, any of them should work.

Is this just a bug in 4.3.1? In EF code first?

Or does it map ForeignKey AND InverseProperty from two different properties to a common third property not supported?

Any information would be greatly appreciated!

Update: second error?

The third option should work (as suggested by Slauma) but throws a NullReferenceException the first time you try to add the object to the database. The database never ends up being created, whereas option 2 from above doesn't have this problem. It looks like it worked for Slauma on EF 4.1, but not for me with EF 4.3.1. (I have been using it since 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; }
}

      

Using:

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

      

+3


source to share


2 answers


Same behavior in EF 4.1.

You did not specify an option to move the attribute InverseProperty

to the other side of the relationship:

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

      

This worked for me and did not create an extra column.



The behavior of your option 2 looks like a code error to me.

Edit

Confirming what version change from EF 4.1 to EF 4.3.1 calls NullReferenceException

with the above model. The database is not created.

+2


source


This was fixed in EF5, and I have confirmed that it still behaves correctly in EF6.



You can see the investigation notes on this issue - https://entityframework.codeplex.com/workitem/138 .

+3


source







All Articles