简体   繁体   中英

How to create one-to-one relationship in Entity Framework Core?

Let's start with one-to-many relationship:

public sealed class MyContext : DbContext
{
  protected override void OnModelCreating(ModelBuilder builder)
  {
    builder.Entity<Slave>()
           .HasOne(typeof(Master))
           .WithMany() // *
           .HasForeignKey(nameof(Slave.ForeignField));
  }
}

So I declare that per one record in Master table I can have multiple records in Slave table. When I run EF tools to build migration this is accepted and works fine.

But when I change the line marked with asterisk to:

           .WithOne()

in order to build one-to-one relationship building migration fails with error:

You are configuring a relationship between 'Slave' and 'Master' but have specified a foreign key on 'ForeignField'. The foreign key must be defined on a type that is part of the relationship.

I don't get it, just a second ago the given field (property in C# terms) was OK, and now EF claims it cannot find it?

Whad do I miss? How to make EF happy?

Record types are as follows -- please note there are no navigational properties.

internal sealed class Slave
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public Guid InstanceId { get; set; }

    public Guid ForeignField { get; set; }
}

internal sealed class Master
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public Guid Id { get; set; }
}

For the time being I solved this using raw SQL, it works, but I am still curious what is wrong here.


Thanks to @AminGolmahalle answer, my curiosity was triggered why and can I use HasForeignKey in generic form. This lead me to finding out that I cannot, but what more is that WithOne and WithMany are not 1:1 replacements of each other. Both lead to different overloads.

So the first version one-to-many worked because I was hitting the right overload, the second didn't, because I was passing incorrect arguments. The correct version is:

builder.Entity<Slave>()
       .HasOne(typeof(Master))
       .WithOne() 
       .HasForeignKey(nameof(Slave), nameof(Slave.ForeignField)); // changed

the first argument has to be name of the slave table (again).

But is is even better to switch to generic version (see last comment under accepted answer) and avoid possibility of such "stupid" mistake in the first place.

Below code just sample for relation one to one:

public class Author
{
    public int AuthorId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public AuthorBiography Biography { get; set; }
}
public class AuthorBiography
{
    public int AuthorBiographyId { get; set; }
    public string Biography { get; set; }
    public DateTime DateOfBirth { get; set; }
    public string PlaceOfBirth { get; set; }
    public string Nationality { get; set; }
    public int AuthorRef { get; set; }
    public Author Author { get; set; }
}

You Can Use FluentApi For Relation In EntityFramework:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Author>()
        .HasOne(a => a.Biography)
        .WithOne(b => b.Author)
        .HasForeignKey<AuthorBiography>(b => b.AuthorRef);
}

Using FluentApi is much better than DataAnnotaion .

FluentApi In Asp Core

I Suggest To You Read About FluentValidation

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