48

I am trying to reference a foreign key from SpouseId to Id in the Contact table. What is the syntax for doing this? I can't seem to find an example. Thanks.

I have a class like this:

public class Contact
{
    public int Id {get;set;}
    public string Name {get;set;}
    public int? SpouseId {get;set;}
}

EDIT1 Per the link provided by Joel Cunningham and the answer from Morteza I have added some additional code.

ContactMap.cs

public partial class ContactMap : EntityTypeConfiguration<Contact>
{
  public ContactMap()
     {
       this.ToTable("Contact");
       this.HasKey(c => c.Id);
       this.HasOptional(c => c.Spouse)
           .WithMany()
           .IsIndependent()
           .Map(m => m.MapKey(fk => fk.Id, "SpouseId"));
     }
}

MyObjectContext.cs

public class MyObjectContext : DbContext, IDbContext
{
  public DbSet<Contact> Contacts {get;set;}
  protected override void OnModelCreating(ModelBuilder modelBuilder)
     {
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        modelBuilder.Configurations.Add(new ContactMap());
     }
}

Note: I also added the "[ForeignKey("SpouseId")]" attribute to my Spouse property in my Contact class. Unfortunately I keep getting "Sequence contains more than one matching element".

EDIT2: Morteza's answers below is correct. To summarize: For self referencing foreign keys you can either mark the property as a "[ForeginKey("SpouseId")] OR use the fluent API example below. The errors I reported in some of my comments were caused by my unit test. EF generated the db the correct way. I found a good link where Craig Stuntz outlined why auto-increment keys and self-referencing foreign keys can cause the "Unable to determine a valid ordering for dependent operations" error. I believe this is what my problem is. Hope this helps someone.

Community
  • 1
  • 1
trevorc
  • 3,023
  • 4
  • 31
  • 49

4 Answers4

72

Something like this will work:

public class Contact
{
    public int Id {get;set;}
    public string Name {get;set;}
    public int? SpouseId {get;set;}

    [ForeignKey("SpouseId")]
    public Contact Spouse {get;set;}
}

ForeignKeyAttribute has been added to System.ComponentModel.DataAnnotations by CTP5 assembly.

Update I: CTP5 Bug:

Due to a bug in CTP5, creating an Independent Self Referencing Associations throws an exception. The workaround is to use Foreign Key Associations instead (which is always recommended regardless).

Update II: Using Fluent API to Configure a Self Referencing Association:

You can also use fluent API to achieve this, if you prefer:

public class Contact
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int? SpouseId { get; set; }                

    public Contact Spouse { get; set; }
}

public class Ctp5Context : DbContext
{
    public DbSet<Contact> Contacts { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Contact>()
                    .HasOptional(c => c.Spouse)
                    .WithMany()
                    .HasForeignKey(c => c.SpouseId);
    }
}

Working with the Model:

using (var context = new Ctp5Context())
{
    Contact contact = new Contact()
    {
        Name = "Morteza",
        Spouse = new Contact()
        {
            Name = "Code-First"
        }
    };
    context.Contacts.Add(contact);
    context.SaveChanges();
}
Morteza Manavi
  • 33,026
  • 6
  • 100
  • 83
  • 1
    @Morteza. I keep getting "Sequence contains more than one matching element". I used the mapping syntax you provided in the linked answer above as well. I know this is necessary because without it EF throws erros complaining about not knowing the foreign key relationships. – trevorc Jan 27 '11 at 15:21
  • None of the fluent API code you wrote in `ContactMap` class in necessary. Only `ForeignKeyAttribute` I showed would be sufficient. Again, you cannot have a Independent Association in this case due to the bug I said in my updated answer. – Morteza Manavi Jan 27 '11 at 16:18
  • 1
    Ok. I misunderstood your answer. I thought your answer was a workaround for the bug. I usually prefer not to use attributes in my POCO's so I will create a mapping table for my needs now. Will the next release fix this bug and allow for this type of mapping? FYI your help on CTP5 on SO and other places is VERY helpful. Thanks. – trevorc Jan 27 '11 at 16:47
  • One other note. I was unable to get your example working even after taking out the fluent API code for SpouseId. I kept getting " Unable to determine a valid ordering for dependent operations". – trevorc Jan 27 '11 at 16:53
  • I updated my answer to show how you can do it without using data annotations. And yes, EF team is aware of this bug and will make sure that this is fixed in the RTM. BTW, I cannot reproduce the exception you are getting, could you post your code that cause this ` Unable to determine a valid ordering for dependent operations` exception? Thanks. – Morteza Manavi Jan 27 '11 at 17:32
  • BTW, thanks for your comment regarding my Code First answers, I am glad they could help. Also check out my EF Code First articles on my weblog, as you might find them useful as well :) – Morteza Manavi Jan 27 '11 at 17:39
  • Thanks for hanging in there Morteza. I believe your answer is working for me because my db is being generated correctly but the error I reported in my previous comment is coming from my unit test. Your second edit appears to address this so I am playing with the syntax of testing for SpouseId.ShouldEqual(1). This is what, I believe is causing the "valid ordering" error. I will continue testing and post anything else I find relevant. – trevorc Jan 27 '11 at 17:45
  • EF6 - MVC5 using a codefirst model requires the 'Update II' solution, great help, thank you. – AndyM Jul 16 '14 at 11:47
  • Is the code in `OnModelCreating` necessary? Why you are using `WithMany()` in the function? Isn't the spouse function a optional one-to-one relationship (assuming Monogamy) – Isaac Jun 08 '16 at 08:27
  • It's a unidirectional one to many association (not a 1:1). If it was bidirectional, then there would be a `public IList Contacts { get; set; }` on the Contact object. – Morteza Manavi Jun 08 '16 at 13:26
  • same thing not compiling with entity framework core. my reference post is https://stackoverflow.com/questions/65681454/how-to-fill-parent-object-of-the-same-table-in-entity-framework-core – Kamran Shahid Jan 12 '21 at 12:52
9
[Table("Move")]
public class Move
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public long ID { get; set; }
    public long? ParentID { get; set; }

    [InverseProperty("Children")]
    public virtual Move Parent { get; set; }
    public virtual ICollection<Move> Children { get; set; }
}
Ali Bah
  • 151
  • 2
  • 4
4

Also, the navigation property Spouse should be virtual to avoid unnecessary JOIN queries:

 public virtual Contact Spouse { get; set; }
ReflexiveCode
  • 256
  • 3
  • 7
  • 1
    Could you explain why must be virtual? – c0demaster Nov 26 '15 at 02:27
  • 3
    It's not _required_ to be virtual, but will save unnecessary JOIN queries by enabling lazy loading for the Spouse object. See [link](http://stackoverflow.com/questions/11469432/entity-framework-code-first-lazy-loading). Edited my response to clarify. – ReflexiveCode Dec 09 '15 at 17:28
  • 2
    @ReflexiveCode Are you saying that without `virtual`, the `Spouse` will be loaded? I think you need `Include` to eagerly load that. – kazinix Mar 12 '18 at 17:11
0
_ = builder.HasMany(e => e.Children)
            .WithOne(e => e.Parent)
            .HasForeignKey(e => e.ParentId);

Above code worked for me

CodeByAk
  • 139
  • 5