Search code examples
c#entity-frameworkef-code-firstforeign-key-relationshipone-to-one

One-to-one relationsip with optional dependent end using default conventions


I'd like to have a principal entity (Person) with optional dependent entity (Car) mapped using the default conventions in Entity Framework code-first model.

One solution is described in this answer, which uses the fluent API on modelBuilder to map to the key.

Is it possible to do this using only the default EF conventions? The following code will throw a Unable to determine the principal end of an association between the types Person and Car. invalid operation exception.

public DatabaseContext : DbContext
  public DbSet<Person> Persons { get; set; }
  public DbSet<Car> Cars { get; set; }
  // No OnModelCreating - possible?

public class Person
  public Int32 PersonId { get; set; }
  public Int32? CarId { get; set; } // Optional.
  public virtual Car Car { get; set; }

public class Car
  public Int32 CarId { get; set; }
  public Int32 PersonId { get; set; }
  public virtual Person Person { get; set; }

This example assumes a person can only be associated with none or one cars. I should not be able to insert a car without an associated person, but I should be able to insert a person without an associated car. Why will EF not figure out that CarId on person is a foreign key for Car and vice versa?

I am using Entity Framework 6.1.2 on .NET Framework 4.5.

Fluent API shortcomings

When I attempt to define the relationship using fluent API, it works, but I am unable to map the key to an existing column, which is why I want to use conventions in the first place. The exception I get with this is Each property name in a type must be unique..

modelBuilder.Entity<Person>().HasOptional(p => p.Car).WithRequired(c => c.Person).Map(a => a.MapKey("PersonId"));

If I leave out the MapKey call, SaveChanges succeeds, but database inspection reveals that the Car row's PersonId foreign key column contains 0, instead of 1, which is the PersonId primary key column in the Person row.


Solution

  • One-to-one foreign key associations are not supported by Entity Framework. You must remove the foreign key properties and use the navegation properties. Map you relationship this way:

    modelBuilder.Entity<Person>().HasOptional(p => p.Car).WithRequired(c => c.Person);
    

    EF will create the FK for you:

    enter image description here

    This is because of Entity Framework’s requirement that the primary key of the dependent (Person) be used as the foreign key.