5

I am using EF Core 2.1

How to map one-to-one relationship in EF Core. I have Customer & Course domain entity where one customer will have one Course.

This is my how my Customer & CoursePOCO looks like.

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }

}

public class Course
{
    public int Id { get; set; }
    public string CouseName { get; set; }
    public virtual Customer Customer { get; set; }
}

I am using FluentAPI.

 public class CourseConfiguration : IEntityTypeConfiguration<Course>
{
    public void Configure(EntityTypeBuilder<Parent> builder)
    {
        builder.HasKey(x => x.Customer.Id) //not allowing -> throws error
        //The properties expression 'x => Convert(x.Customer.Id, Object)' is not valid. 
        // The expression should represent a simple property access: 't => t.MyProperty'. 
        // When specifying multiple properties use an anonymous type: 't => new { t.MyProperty1, t.MyProperty2 }'. 
        // Parameter name: propertyAccessExpression
    }
}

Since it's one to one relation, I don't want to create an extra key in Contact (FK -CustomerId),

Primary requirement:- I want to define Id (in Course) as PK + FK & in this relation Customer is parent entity.

Like if I was Configuration based Migration, I would do as follows:-

public class Course
{
    [Key]
    [ForeignKey("Customer")]
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual Customer Customer { get; set; }
 }

same thing I would like to do using Fluent API in EF Core??

Thanks!!

Kgn-web
  • 7,047
  • 24
  • 95
  • 161
  • Just curious on why do you want to have the customer fk as Id on Course? Course is something generic.. instead I think a better model would be a Customer have a course, don't you think? Your model become simpler this way, and, if later you decide to tie you Course with something else, you are safe. – jpgrassi Dec 31 '18 at 11:09

2 Answers2

8

As other answers point out, the key point is to use use the HasForeignKey<>() method to configure a foreign key.

But be careful that the foreign key should be set on the dependent entity, instead of the principal entity.

How to in details:

Add a navigation property to Course for Customer

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Course Course {get;set;} 
}

and now set the Course.Id as a FK that references the Customer.Id

public class AppDbContext : DbContext
{
    public AppDbContext (DbContextOptions<AppDbContext> options)
        : base(options)
    {
    }
    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        builder.Entity<Customer>(entity=>
        {
            entity.HasOne(customer=>customer.Course)
                .WithOne(course=> course.Customer)
                .HasForeignKey<Course>(course=>course.Id); 
        });
    }

    public DbSet<App.Models.Customer> Customer { get; set; }
    public DbSet<App.Models.Course> Courses{ get; set; }
}

the generated sql scripts is :

CREATE TABLE [Customer] (
    [Id] int NOT NULL IDENTITY,
    [Name] nvarchar(max) NULL,
    CONSTRAINT [PK_Customer] PRIMARY KEY ([Id])
);

GO

CREATE TABLE [Courses] (
    [Id] int NOT NULL,
    [CouseName] nvarchar(max) NULL,
    CONSTRAINT [PK_Courses] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Courses_Customer_Id] FOREIGN KEY ([Id]) REFERENCES [Customer] ([Id]) ON DELETE CASCADE
);

GO
itminus
  • 23,772
  • 2
  • 53
  • 88
  • Thanks for highlight the principal entity point, but still the primary requirement is unaddressed. I don't want to have Id & Customer as PK & FK respectively instead Id in course should server as PK & FK both – Kgn-web Dec 31 '18 at 12:05
  • @Kgn-web I don't create a PK & FK respectively. the Course.Id serves as PK and FK at the same time. – itminus Dec 31 '18 at 12:06
  • @Kgn-web you could inspect the sql scripts above (generated by `dotnet ef migrations script`) to confirm that `Courses.Id` serves as PK and FK at the same time. – itminus Dec 31 '18 at 12:11
  • Checking in progress,Please allow me some minutes Thanks – Kgn-web Dec 31 '18 at 12:12
  • cool. Yes. your post addressed my requirement.. Thanks – Kgn-web Dec 31 '18 at 12:18
  • Thanks for the revision. However, IMO, the revision is incorrect. So I roll it back. Correct me if you believe I'm wrong :) – itminus Sep 07 '20 at 14:11
3

Something similar to below should help. HasOne method is chained with WithOne would help to establish one-to-one relationship :

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }

}

public class Course
{
    public int Id { get; set; }
    public string CourseName { get; set; }
    public int CustomerId {get;set;}
    public virtual Customer Customer { get; set; }
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Course>()
        .HasOne(a => a.Customer)
        .WithOne(b => b.Course)
        .HasForeignKey<Course>(b => b.CustomerId);
}
Manoj Choudhari
  • 5,277
  • 2
  • 26
  • 37