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

1

3

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.

Collard answered 10/1, 2015 at 10:53 Comment(1)
I've now learnt that one-to-one in EF behaves in such a way that the foreign key must match the primary key, which explains why EF can pull out associations from seemingly thin air when using fluent API and doesn't fill any column with the value of the primary key of the other end.Nucleolar
C
4

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.

Crookes answered 10/1, 2015 at 22:48 Comment(1)
Thanks, I am using this at the moment.Nucleolar

© 2022 - 2024 — McMap. All rights reserved.