1

Let's start with a textbook example for Blogs and Posts using code-first EntityFramework Core:

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public ICollection<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}

The EF Core configurures the one-to-many relationship automatically by convension, or it can be done manually using fluent API:

internal class MyContext : DbContext
{
    // ...

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Post>()
            .HasOne(p => p.Blog)
            .WithMany(b => b.Posts);
            .HasForeignKey(p => p.BlogId);
    }
}

Fine. Now I would like to add an optional FeaturedPost to a Blog.

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public ICollection<Post> Posts { get; set; }
    
    public Post FeaturedPost { get; set; }
}

What would be the recommended way of configuring such additional relationship (preserving the original one-to-many relationship) in EF Core? Automatic confuguration results in exception and I fail to figure out how to achieve this manually.

seilgeir
  • 13
  • 3

1 Answers1

2

You could take a look to this other answer of mine.

But, in short, if you have multiple relationships between two types you will need to configure them using the Fluent API.

In your case, try something like this:

internal class MyContext : DbContext
{
    // ...

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Post>()
            .HasOne(p => p.Blog)
            .WithMany(b => b.Posts);
            .HasForeignKey(p => p.BlogId);

        modelBuilder.Entity<Blog>()
            .HasOne(x => x.FeaturedPost)
            .WithOne()
            .HasForeignKey<Blog>(b => b.FeaturedPostId); 

        // You should add a int? FeaturedPostId property in your Blog class.
        // Having a nullable FK will make it optional.

    }
}
dglozano
  • 6,369
  • 2
  • 19
  • 38
  • I would recommend a nullable `FeaturedPostId`. If posts require a blog and blogs require a post, maintaining referencial integrity will always be a nightmare. – grek40 Mar 30 '21 at 07:11
  • 1
    @grek40 not only that, but also the OP asked for an optional `FeaturedPostId` explicitly , completely forgot about that. Have updated my answer, thank :) – dglozano Mar 30 '21 at 07:25
  • 1
    Indeed @grek40 making `FeaturedPostId` nullable was what I've been missing, that fixed the model :-) – seilgeir Mar 30 '21 at 07:50
  • @seilgeir yes, if you don't make it nullable it won't be optional, and then you have a chicken-and-egg problem when saving (Post requires BlogId, and Blog requires FeaturedPostId, but none are created yet). Making one optional solves the issue because Post can be created without having the FK yet. Btw there is a good answer related to that https://stackoverflow.com/a/42734093/10648865 – dglozano Mar 30 '21 at 07:53
  • @dglozano I was confused because I have (among other things) tried to configure the relation with `.IsRequired(false)` but that did not make any change (the field in the database was still created as `NOT NULL`). Shouldn't this `.IsRequired(false)` throw if the corresponding foreign key property is not nullable? – seilgeir Mar 30 '21 at 08:52