One-to-one relationship in EF Core gives child/dependent exception

c# entity-framework-core

Question

I have two entities that represent a User of the web application, and a Participant in the podcast that the web application is all about. A user of the web page has a profile, can log in, can leave comments etc. A Participant is linked from the episode objects they appear in, has a bio, a picture etc. It is possible to be both a Participant and a User at the same time, but it is also possible to be just a user, or just a participant.

I'm struggling to model this in EF Core 3.1. If it matters, I am also using .Net Core 3.0 for this project, and the database is Postgresql (using the Nuget package Npgsql.EntityFrameworkCore.PostgreSQL v3.1.0).

On both sides this relationship should be nullable/non-required. The entities are pretty simple (all non-importart properties omitted):

User:

public class User
{
    public Guid UserId { get; set; }
    public Participant Participant { get; set; }

    public Guid ParticipantId { get; set; }
}

Participant:

public class Participant
{
    public Guid ParticipantId { get; set; }

    public User User { get; set; }

    public Guid UserId { get; set; }

}

I am trying to use the Fluent API to configure the relationships - this seems to be where it breaks down.

User config:

public class UserConfiguration : IEntityTypeConfiguration<User>
{
    public void Configure(EntityTypeBuilder<User> user)
    {
        user.ToTable("users");

        user.HasKey(u => u.UserId);

        user.HasOne(u => u.Participant)
            .WithOne(p => p.User)
            .HasForeignKey<Participant>(p => p.UserId);

    }
}

Participant config:

public class ParticipantConfiguration : IEntityTypeConfiguration<Participant>
{
    public void Configure(EntityTypeBuilder<Participant> participant)
    {
        participant.ToTable("participants");

        participant.HasKey(p => p.ParticipantId);

        participant.HasOne<User>(p => p.User)
            .WithOne(u => u.Participant)
            .HasForeignKey<User>(u => u.ParticipantId);
    }
}

Now I realize that you should only configure one side of the relationship - at least that is how I interpret what I have read. I have just included the above for completeness; I have tried configured both sides at once as above, I have tried doing it only on the User side, and only on the Participant side. In every combination, the code compiles, and the application starts up, but when I try actually adding a User to the database through the DbContext, I get the same exception:

System.InvalidOperationException: 'The child/dependent side could not be determined for the one-to-one relationship between 'Participant.User' and 'User.Participant'. To identify the child/dependent side of the relationship, configure the foreign key property. If these navigations should not be part of the same relationship configure them without specifying the inverse. See http://go.microsoft.com/fwlink/?LinkId=724062 for more details.'

Now, these are two completely independent objects that happen to know of each other, so I am not sure the mindset of child/dependent is accurate, but I am willing to ignore that detail to bend to EF Core's will; However, I can't figure out how to let me get this working without the exception.

TL;DR:

  • A User can have a Participant link.
  • A Participant can have a User link.
  • It is perfectly fine for either for that link to be NULL.
  • How do I configure EF Core for this?

Thanks for any insights!

1
2
12/15/2019 7:44:29 PM

Accepted Answer

I would double check that your configuration is being called. Everything looks like it should work.

1
12/16/2019 6:12:00 AM

Popular Answer

First thing to do i think is to change foreign keys into nullables.

public class User
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Key]
    public Guid UserId { get; set; }

    public Participant Participant { get; set; }

    public Guid? ParticipantId { get; set; }
}

public class Participant
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Key]
    public Guid ParticipantId { get; set; }

    public User User { get; set; }

    public Guid? UserId { get; set; }
}

And then keep your configurations intact. My sample working configuration :

 protected override void OnModelCreating(ModelBuilder modelBuilder)
 {
            modelBuilder.Entity<User>().HasOne(t => t.Participant)
                     .WithOne(t => t.User)
                      .HasForeignKey<Participant>(t => t.UserId);

            modelBuilder.Entity<Participant>().HasOne(t => t.User)
                     .WithOne(t => t.Participant)
                     .HasForeignKey<User>(t => t.ParticipantId);
            base.OnModelCreating(modelBuilder);
 }


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow