O'Reilly logo

Programming Entity Framework: Code First by Rowan Miller, Julia Lerman

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Working with One-to-One Relationships

There is one type of relationship that Code First will always require configuration for: one-to-one relationships. When you define a one-to-one relationship in your model, you use a reference navigation property in each class. If you have a reference and a collection, Code First can infer that the class with the reference is the dependent and should have the foreign key. If you have two collections, Code First knows it’s many-to-many and the foreign keys go in a separate join table. However, when Code First just sees two references, it can’t work out which class should have the foreign key.

Let’s add a new PersonPhoto class to contain a photo and a caption for the people in the Person class. Since the photo will be for a specific person, we’ll use PersonId as the key property. And since that is not a conventional key property, it needs to be configured as such with the Key Data Annotation (Example 4-20).

Example 4-20. The PersonPhoto class

using System.ComponentModel.DataAnnotations;
 namespace Model
{
  public class PersonPhoto
  {
    [Key]
    public int PersonId { get; set; }
    public byte[] Photo { get; set; }
    public string Caption { get; set; }

    public Person PhotoOf { get; set; }
  }
}

Let’s also add a Photo property to the Person class, so that we can navigate both directions:

public PersonPhoto Photo { get; set; }

Remember that Code First can’t determine which class is the dependent in these situations. When it attempts to build the model, an exception is thrown, telling you that it needs more information:

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

This problem is most easily solved by using a ForeignKey annotation on the dependent class to identify that it contains the foreign key. When configuring one-to-one relationships, Entity Framework requires that the primary key of the dependent also be the foreign key. In our case PersonPhoto is the dependent and its key, PersonPhoto.PersonId, should also be the foreign key. Go ahead and add in the ForeignKey annotation to the PersonPhoto.PersonId property, as shown in Example 4-21. Remember to specify the navigation property for the relationship when adding the ForeignKey annotation.

Example 4-21. Adding the ForeignKey annotation

public class PersonPhoto
{
  [Key]
  [ForeignKey("PhotoOf")]
  public int PersonId { get; set; }
  public byte[] Photo { get; set; }
  public string Caption { get; set; }

  public Person PhotoOf { get; set; }
}

Running the application again will successfully create the new database table, although you’ll see that Entity Framework didn’t deal well with pluralizing the word “Photo.” We’ll clean that up in Chapter 5, when you learn how to specify table names. More importantly, notice that PersonId is now both a PK and an FK. And if you look at the PersonPhoto_PhotoOf foreign key constraint details, you can see that it shows the People.PersonId is the primary table/column in the relationship and PersonPhotoes.PersonId is the foreign key table/column (Figure 4-14). This matches our intent.

PersonPhotoes with foreign key

Figure 4-14. PersonPhotoes with foreign key

Earlier in this chapter, we also saw that you could place the ForeignKey annotation on the navigation property and specify the name of the foreign key property (in our case, that is PersonId). Since both classes contain a PersonId property, Code First still won’t be able to work out which class contains the foreign key. So you can’t employ the configuration in that way for this scenario.

Of course, there is also a way to configure this in the Fluent API. Let’s assume for the moment that the relationship is one-to-zero-or-one, meaning a PersonPhoto must have a Person but a Person isn’t required to have a PersonPhoto. We can use the HasRequired and WithOptional combination to specify this:

modelBuilder.Entity<PersonPhoto>()
  .HasRequired(p => p.PhotoOf)
  .WithOptional(p => p.Photo);

That’s actually enough for Code First to work out that PersonPhoto is the dependent. Based on the multiplicity we specified, it only makes sense for Person to be the principal and PersonPhoto to be the dependent, since a Person can exist without a PersonPhoto but a PersonPhoto must have a Person.

Notice that you didn’t need to use HasForeignKey to specify that PersonPhoto.PersonId is the foreign key. This is because of Entity Framework’s requirement that the primary key of the dependent be used as the foreign key. Since there is no choice, Code First will just infer this for you. In fact, the Fluent API won’t let you use HasForeignKey. In IntelliSense, the method simply isn’t available after combining HasRequired and WithOptional.

Configuring One-to-One Relationships When Both Ends Are Required

Now let’s tell Code First that a Person must have a PersonPhoto (i.e., it’s required). With Data Annotations, you can use the same Required data annotation that we used earlier on Destination.Name and Lodging.Name. You can use Required on any type of property, not just primitive types:

[Required]
public PersonPhoto Photo { get; set; }

Now update the Main method to call the InsertPerson method you defined back in Chapter 3 and run the application again. An exception will be thrown when SaveChanges is called. In the exception, Entity Framework’s Validation API reports that the validation for the required PersonPhoto failed.

Ensuring that the sample code honors the required Photo

If you want to leave the Photo property as Required and avoid the validation errors, you can modify the InsertPerson and UpdatePerson methods so that they add data into the Photo field. For the sake of keeping the code simple, we’ll just stuff a single byte into the Photo’s byte array rather than worrying about supplying an actual photo.

In the InsertPerson method, modify the line of code that instantiates a new Person object to add the Photo property, as shown in Example 4-22.

Example 4-22. Modifying the InsertPerson method to add a Photo to the new Person

var person = new Person
{
  FirstName = "Rowan",
  LastName = "Miller",
  SocialSecurityNumber = 12345678,
  Photo = new PersonPhoto { Photo = new Byte[] { 0 } }
};

In the UpdatePerson method, we’ll add some code to ensure that any Person data you’ve already added before we created the Photo class gets a Photo at the same time that you update. Modify the UpdatePerson method as shown in Example 4-23 so that it allocates a new PersonPhoto when it tries to update a person without a photo.

Example 4-23. Modification to UpdatePerson to ensure existing Person data has a Photo

private static void UpdatePerson()
{
  using (var context = new BreakAwayContext())
  {
    var person = context.People.Include("Photo").FirstOrDefault();
    person.FirstName = "Rowena";
    if (person.Photo == null)
    {
      person.Photo = new PersonPhoto { Photo = new Byte[] { 0 } };
    }

    context.SaveChanges();
  }
}

The updated method will use Include to also retrieve the Person’s Photo when fetching the data from the database. We then check if the Person has a Photo and add a new one if they do not. Now the Photo requirement in the Person class will be fulfilled any time you execute the InsertPerson and UpdatePerson methods.

Configuring one-to-one with the Fluent API

Not surprisingly, you can also configure the same relationship with the Fluent API. But you’ll need to let Code First know which class is the principal and which is the dependent. If both ends are required, this can’t simply be implied from the multiplicity.

You might expect to call HasRequired followed by WithRequired. However, if you start with HasRequired, you will have the additional options of WithRequiredPrincipal and WithRequiredDependent in the place of WithRequired. These methods take into account the entity that you are configuring; that is, the entity that you selected in modelBuilder.Entity or the entity that your EntityTypeConfiguration class is for. Selecting WithRequiredPrincipal will make the entity that you are configuring the principal, meaning it contains the primary key of the relationship. Selecting WithRequiredDependent will make the entity that you are configuring the dependent, meaning it will have the foreign key of the relationship.

Assuming you are configuring PersonPhoto, which you want to be the dependent, you would use the following configuration:

modelBuilder.Entity<PersonPhoto>()
  .HasRequired(p => p.PhotoOf)
  .WithRequiredDependent(p => p.Photo);

Configuring a one-to-one relationship where both ends are optional works exactly the same, except you start with HasOptional and select either WithOptionalPrincipal or WithOptionalDependent.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required