简体   繁体   中英

Entity Framework 6 Navigation property alias?

I have these two classes:

public class Foo
{
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
    // ...

    // Foo has N bars, 1 of which is primary
    [ForeignKey("Bar")]
    public int? PrimaryBarId { get; set; }
    public virtual Bar PrimaryBar { get; set; }
}

public class Bar {
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
    // ...

    // Foo -> Bar == 1:N
    [ForeignKey("Foo")]
    public int FooId { get; set; }
    public virtual Foo Foo { get; set; }
}

EF now expects me to create a property BarId and Bar on Foo but I don't want that. I want the property to be named Primary Bar. I have a 1:N relationship on Foo -> Bar which is reflected elsewhere and not of interest for this question.

EF keeps telling me:

The ForeignKeyAttribute on property 'PrimaryBarId' on type 'XyFoo' is not valid. The navigation property 'Bar' was not found on the dependent type 'XyFoo'. The Name value should be a valid navigation property name.

How do I convince EF to use the PrimaryBar (and PrimaryBarId ) properties (preferrably with an attribute, although using the DbModelBuilder in the OnModelCreating override is an option too?

EDIT

Figured it out. I was missing a:

public virtual ICollection<Bar> Bar { get; set; }

on my Foo class. See here for the explanation.

As per the documentation , the Name provided to the ForeignKeyAttribute should be the property name not the type or table name. So change your code to this:

public int? PrimaryBarId { get; set; }
[ForeignKey("PrimaryBarId")]
public virtual Bar PrimaryBar { get; set; }

Or:

[ForeignKey("PrimaryBar")]
public int? PrimaryBarId { get; set; }
public virtual Bar PrimaryBar { get; set; }

First things first.

If your FK property is a nullable int like I see in your code, your relationship will be 0..1-N and not 1-N as it is a nullable foreign key.

Second, I am not very familiar with attribute syntax as it's not ideal to describe your model, and it clutters your object with EF related data. The preferred approach is to declare EF mappings in a separate class which inherits EntityTypeConfiguration<T> where T is your class.

Now given your classes, first of all you must add a property on Bar that maps the N reference, like this:

public virtual ICollection<Foo> Foos {get;set;}

Then you can declare an EntityTypeConfiguration<Bar> that, among other settings like defining the primary key and property->column name translation if they don't match, will contain:

this
  .HasMany(p => p.Foos)
  .WithOptional(p => p.PrimaryBar)
  .HasForeignKey(p => p.PrimaryBarId);

If your FK was an int instead of int? you would have used WithRequired instead of WithOptional .

According to MSDN Name parameter is not the entity name but the navigation property name (in your case, as it's a bit more complicated than that).

You should change your code from:

[ForeignKey("Bar")]

to:

[ForeignKey("PrimaryBar")]

I'm terribly sorry, but none of the answers were correct. That is: they are correct (probably) but I posted my example code for the question wrong. First I forgot to rename some types/properties, then I found a mistake in the the errormessage I posted. And finally it turns out I forgot to post the following code I had on my Bar class:

class Bar {
    //Didn't post this code:
    [ForeignKey("Foo")]
    public int FooId { get; set; }
    public virtual Foo Foo { get; set; }
}

This code was for the 1:N Foo has to Bar . But since the PrimaryBar property implies a 1:0..1 relation EF got confused I guess. What I was missing was the following on my Foo class to contain the 1:0..N with Bar :

class Foo {
    public virtual ICollection<Bar> Bars { get; set; }
}

I added this collection et voila; everything works fine now.

Oh, I did have to change the ForeignKey to PrimaryBar instead of Bar indeed as most answers suggested.

I'm terribly sorry and mea culpa: all f*ckups were mine and my own only. I usually don't prefer to post "Foo/Bar/Baz" code instead of actual code but in this case it was a bit difficult and the classes would raise (unrelated) questions on their own which I didn't want to discuss :P

I have upvoted all answers as a "thank you"; however since none of them was the actual solution, again because of my being a dumbass and posting incorrect code/information, I have posted my own answer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM