Ajay Bhosale
Ajay Bhosale

Reputation: 2002

Entity Framework - Code First : Mapping with Mutliple child/optional properties of same type

My domain model has User and Address Class

public User
{
   int Id
   Address PrimaryAddress
   Address SecondaryAddress
   and some other Properties
}

Both are optional, but every address must have a user. Codeirst Mapping looks like

modelBuilder.Entity<User>()
   .HasOptional(u => u.PrimaryAddress)
   .WithRequired()
   .WillCascadeOnDelete(false);
modelBuilder.Entity<User>()
   .HasOptional(u => u.SecondaryAddress)
   .WithRequired()
   .WillCascadeOnDelete(false);

But this is not generating desired result. I am expecting two (int, null) columns for User Table pointing to Address Table. And Address table probably having a UserId (Foreign Key, int, not null) column. But with above code The "ID" column of Address acting as PK and FK (pointing back to User table).

Upvotes: 0

Views: 1102

Answers (1)

Ladislav Mrnka
Ladislav Mrnka

Reputation: 364269

You have mapped one-to-one relation and that is exactly what you see in the database. In short what you want to achieve is not possible this way.

Especially the part where Address must have a User cannot work because you have two navigation properties on User entity which will result in two different FK columns in Address table and they will have to be nullable otherwise each address will have to be both primary and secondary for some user.

Correct model is mapped with default mapping (no fluent mapping is needed) because it creates user with two FKs to address. Address is in two one-to-many relations with user because indeed in theory you can have multiple users with the same address.

Another options is to model the relation as many-to-many where junction table will be modeled as entity with additional property AddressType.

If you really want to follow your current model you must use this:

modelBuilder.Entity<User>()
    .HasOptional(u => u.PrimaryAddress)
    .WithOptionalPrincipal()
    .WillCascadeOnDelete(false);
modelBuilder.Entity<User>()
    .HasOptional(u => u.SecondaryAddress)
    .WithOptionalPrincipal()
    .WillCascadeOnDelete(false);

It will create two additional FK columns in Address table and the relation itself will be one-to-many (one user can have many addresses). The reason is that enforcing one-to-one requires uniqueness of the FK and current EF version doesn't support unique keys. You can add unique constraints on FK columns in post processing (either through custom DB initializer or with EntityFramework.Migrations) but it will result in other problems. Null value is in SQL considered as valid value so if you have unique constraint on nullable column only single record can have null value.

Upvotes: 1

Related Questions