Home > front end >  EF Core mistaking property as PK because of name
EF Core mistaking property as PK because of name

Time:01-28

I have an entity named User and a value object named UserRef containing UserId.

EF is picking up the UserId on the UserRef and trying to map it to a User.

This isn't intended as this entity is for DDD, and I have no interest in using it as a navigational property.

If I rename the UserRef property to User_Id, everything works fine.

How can I tell EF to leave the property alone, and stop trying to do anything with it (other than read/write its value)?

public class User
{
    public Guid Id { get; private set; }

    public UserRef? ArchivedByUser { get; private set; }
}

public class UserRef
{
    public Guid UserId { get; private set; }
}

public class UserConfiguration : IEntityTypeConfiguration<User>
{
    public void Configure(EntityTypeBuilder<User> builder)
    {
        builder.HasKey(o => o.Id);

        builder.OwnsOne(o => o.ArchivedByUser, archivedByUser => {
            archivedByUser.Property(userRef => userRef.UserId)
                            .HasColumnName("ArchivedByUser");
        });

    }
}

I get the following error:

The keys {'UserId'} on 'User.ArchivedByUser#UserRef' and {'Id'} on 'User' are both mapped to 'User.PK_User' but with different columns ({'ArchivedByUser'} and {'Id'}).

Thanks!

CodePudding user response:

Owned entities have implicit key (shared PK/FK of one-to-one relationship with owner) which by convention is shadow property named <owner entity name><owner entity key name>.

Which in this particular case is UserId and matches the class property with the same name and type, so EF decides that it should be used for the aforementioned purpose.

As usual when EF conventions do not work, you resort to explicit configuration. In this case it is enough to specify different shadow property name to be used as PK/FK. e.g.

archivedByUser.WithOwner().HasForeignKey("OwnerId");

The name is arbitrary since there is no table column associated with it.

The above fluent API call is enough to achieve the goal. But if you want to be absolutely sure/maximum explicit, then the full configuration is like

const string KeyPropertyName = "OwnerId";
archivedByUser.Property<Guid>(KeyPropertyName);
archivedByUser.HasKey(KeyPropertyName);
archivedByUser.WithOwner().HasForeignKey(KeyPropertyName);
  •  Tags:  
  • Related