One-To-Zero or One relationship

Description Article
Table of Contents
Summary

Configuring  one-to-zero or one relationships with Code First Model.

Description

Database Diagram:

Entity Framework One-To-Zero or One Database Diagram

Entity Data Model:

Entity Framework One-To-Zero or One EDM

Operations
Configuring relationship using DataAnnotation attributes

At database relational model:

  • Primary keys of the parent and child tables are equal;
  • The parent record is inserted first;

At the business object/configuration mapping model:

  • The Key() attribute is used on the KEY property in the child entity object, in order to mark it as primary key, if the property name doesn’t follow Code First conventions for primary keys;
  • The ForeignKey(“PARENTCLASSNAME”) attribute is used on the key property of the child entity object, in order to mark it as primary key as well as foreign key.

If unidirectional relationship, then:

  1. The navigation property to the parent class type in the child entity object can be removed

Due to the Entity Framework’s requirement that the primary key of the “Dependent” be used as the foreign key, Code First will just infer who are the “Principal” and “Dependent” entities.
Thus, no more configuration is required.

If both ends are required (i.e. One-To-One relationship) then the Required() attribute must be used on the parent navigation property.

How to do it

Domain classes definition and mapping:

public class Student
{
  public Student() { }

  public int StudentId { get; set; }
  public string StudentName { get; set; }

  public virtual StudentAddress StudentAddress { get; set; }
}

public class StudentAddress
{
   [Key, ForeignKey("Student")]
   public int StudentId { get; set; }

   public string Address1 { get; set; }
   public string Address2 { get; set; }
   public string City { get; set; }
   public int Zipcode { get; set; }
   public string State { get; set; }
   public string Country { get; set; }

   public virtual Student Student { get; set; }
}
Configuring relationship using Fluent API

At database relational model:

  • Primary keys of the parent and child tables are equal;
  • The parent record is inserted first;
  • At the business object/configuration mapping model:

    • The primary key for the child entity object is configured using the HasKey() method, if it doesn’t follow Code First conventions for primary keys
    • The navigation property in the parent entity object is configured as optional using the HasOptional()/HasRequired() method. Thus the FK is marked as nullable/non-nullable on the child table (i.e. NULL/NOT NULL)
    • Inverse relationship to the parent is created using the WithRequired()/WithOptional() method on the navigation property of the child object.

    If unidirectional, then:

    1. The navigation property to the parent class type in the child entity object can be removed
    2. The WithRequired()/WithOptional() method(s) are replaced by the overload without parameters.

    Due to the Entity Framework’s requirement that the primary key of the “Dependent” be used as the foreign key, Code First will just infer who are the “Principal” and “Dependent” entities. Thus, no more configuration is required.

    If both ends are required (i.e. One-To-One relationship) then

    • The HasRequired() method must be used instead of the HasOptional() method in order to mark the foreign key as non-nullable (i.e. NOT NULL);
    • The WithRequiredPrincipal() must be used instead of the WithRequired() method in order to identify the entity being configured as the “Principal” entity object

    Alternative configuration:

    At the business object/configuration mapping model:

    • The primary key for the child entity object is configured using the HasKey() method, if it doesn’t follow Code First conventions for primary keys
    • The navigation property in the child entity object is configured as required using the HasRequired() method. Thus the FK is marked as non-nullable on the child table (i.e. NULL), and the relantionship become also bidirectional
    • The navigation property in the parent entity object is configured as optional using the WithOptional() method. Thus the relation is marked optional

    If both ends are required (i.e. One-To-One relationship) then:

    1. The WithRequiredDependent() must be used instead of the WithOptional() method in order to identify the entity being configured as the “Principal” entity object
    Remarks

    The combination of HasOptional()/HasRequired() with WithOptional/WithRequired() is not valid because Code First is uncapable of determine who is the “Principal” of the relationship.

    In this scenario he following exception is thrown:

    Unable to determine the principal end of an association between the types ‘XXX’ and ‘YYY’. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.

    The WithRequiredPrincipal() or WithRequiredDependent() methods must be used to identifiy the “Principal” of the relationship.
    The parameterless overload is used for unidirectional navigation.

    How to do it

    Domain classes definition:

    public class Student
    {
      public Student() { }
    
      public int StudentId { get; set; }
      public string StudentName { get; set; }
    
      public virtual StudentAddress StudentAddress { get; set; }
    
    }
    
    public class StudentAddress
    {
      public int StudentId { get; set; }
    
      public string Address1 { get; set; }
      public string Address2 { get; set; }
      public string City { get; set; }
      public int Zipcode { get; set; }
      public string State { get; set; }
      public string Country { get; set; }
    
      public virtual Student Student { get; set; }
    }

    Relationship configuration on DbContext:

    // Configure StudentId as PK for StudentAddress
    modelBuilder.Entity<StudentAddress>()
                .HasKey(sa => sa.StudentId);
    
    // Configure bidirectional relation Student to StudentAddress (One-To-Zero or One)
    modelBuilder.Entity<Student>()
                        .HasOptional(std => std.StudentAddress)
                        .WithRequired(sa => sa.Student);	
    
    // Configure  bidirectional relation Student to StudentAddress (One-To-One)
    modelBuilder.Entity<Student>()
                        .HasOptional(std => std.StudentAddress)
                        .WithOptionalPrincipal(t => t.Student);

    Relationship configuration on DbContext (Alternative configuration):

    // Configure StudentId as PK for StudentAddress
    modelBuilder.Entity<StudentAddress>()
                .HasKey(sa => sa.StudentId);
    
    // Configure bidirectional relation StudentAddress to Student (One-To-Zero or One)
    modelBuilder.Entity<StudentAddress>()
                        .HasRequired(sa => sa.Student)
                        .WithOptional(s => s.StudentAddress); 
    
    // Configure bidirectional relation StudentAddress to Student (One-To-One)
    modelBuilder.Entity<StudentAddress>()
                        .HasRequired(sa => sa.Student)
                        .WithRequiredDependent(t => t.StudentAddress);