When persisting class hierarchies, all properties in derived classes must be nullable.
A common pattern for representing class hierarchies in relational databases is to use a table that will have columns for each of the base and derived classes. For example, imagine we have a Vehicle
base class and two derived classes, LandVehicle
and AirVehicle
:
Using the Table per class hierarchy (also called single table inheritance), this could be represented as follows:
You may have noticed that the columns that correspond to the properties in the derived classes–Wheels
and Wings
–are set to accept nulls. Why is that? Well, since this table has to support a schema for all the possible derived classes, we never know, for each row, which class it will map to. For that reason, all columns that correspond to the properties in derived classes need to be nullable, because otherwise, what value would we store there for the records that correspond to other classes?
Consider the following table per class hierarchy values for example:
ID |
Name |
Wheels |
Wings |
Discriminator |
---|---|---|---|---|
1 |
Helicopter |
NULL |
0 |
|
2 |
Car |
4 |
NULL |
|
All our properties of value types need to be marked as nullable:
public class LandVehicle : Vehicle { public int? Wheels { get; set; } } public class AirVehicle { public int? Wings { get; set; } }
This is for nullable value types; since they cannot be null, the built-in conventions will mark the properties as nullable. For reference types, we need to mark them as such explicitly, in the OnModelCreating
method:
protected override void OnModelCreating( protected override void OnModelCreating( DbModelBuilder modelBuilder) { //create the hierarchy modelBuilder .Entity<Vehicle>() .HasDiscriminator() .HasValue<LandVehicle>(typeof(LandVehicle).Name) .HasValue<AirVehicle>(typeof(AirVehicle).Name); //mark Wheels as nullable – not required modelBuilder .Entity<Vehicle>() .Property(x => x.Wheels) .IsRequired(false); //mark Wings as nullable – not required modelBuilder .Entity<AirVehicle>() .Property(x => x.Wings) .IsRequired(false); base.OnModelCreating(modelBuilder); }
Here we are configuring the class Vehicle
to have two derived classes, AirVehicle
and LandVehicle
, each using as its discriminator value, its own class name. Each property in one of the derived classes is set to not required
(nullable).