Introduction
Working with entity framework code first is interesting. But last week, I faced some problems to configure One to Zero or One relation between entities, especially working with combinations of keywords like HasOptional
with WithRequired
, WithOptionalPrincipal
, WithOptionalDependent
.
So let’s see how we can use combinations of these keywords, to make relation between Tables. And what different structured tables are being populated for being different in combinations.
Background
Let’s say we have two entities, Student
and StudentContact
, where a:
Student
may only have a contact (StudentContact
) or not - But every contact (
StudentContact
) must have a Student
public class Student
{
public long Id { get; set; }
public string Name { get; set; }
public virtual StudentContact Contact { get; set; }
}
public class StudentContact
{
public long Id { get; set; }
public string ContactNumber { get; set; }
public virtual Student Student { get; set; }
}
Option 1: HasOptional then WithRequired
Configure relation: from Student
configuration
Student
configuration:
HasKey(x => x.Id);
Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasOptional(x => x.Contact)
.WithRequired(l => l.Student);
StudentContact
configuration:
HasKey(x => x.Id);
Property(x => x.Id)
.HasColumnName("Student_Id")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
Student
:
Id
is primary key with auto increment
StudentContact
:
Id
(Student_Id
as we customized) is both primary key and foreign key from Student
's Id
column
Option 2: HasOptional then WithOptionalPrincipal
Configure relation: from Student
configuration
Student
configuration:
HasKey(x => x.Id);
Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasOptional(x => x.Contact)
.WithOptionalPrincipal(l => l.Student);
StudentContact
configuration:
HasKey(x => x.Id);
Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Student
:
Id
is primary key with auto increment
StudentContact
:
Id
is primary key with auto increment Student_Id
is automatically created and has no navigation property in StudentContact
/Student
entity Student_Id
is not null
able and foreign key from Student
's Id
column
Option 3: HasOptional then WithOptionalDependent
Configure relation: from Student
configuration.
Student
configuration:
HasKey(x => x.Id);
Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasOptional(x => x.Contact)
.WithOptionalDependent(l => l.Student);
StudentContact
configuration:
HasKey(x => x.Id);
Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Student
:
Id
is primary key with auto increment Contact_Id
is automatically created and has no navigation property in StudentContact
/Student
entity Contact_Id
is null
able foreign key from StudentContact
’s Id
column
StudentContact
:
Id
is primary key with auto increment
Well, some books described it like:
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.
Interesting Thing !!!
Until now, we have configured relation from Student
configuration, but let’s say we want to configure relations from the Student Contact
configuration, so what may we do … !!!
Option1Mimic: HasRequired then WithOptional
Configure relation: from StudentContact
configuration.
StudentContact
configuration:
HasKey(x => x.Id);
Property(x => x.Id)
.HasColumnName("Student_Id")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
HasRequired(x => x.Student)
.WithOptional(l => l.Contact);
Student
configuration:
HasKey(x => x.Id);
Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
which would be the same as Option1
:
Option2Mimic: HasRequired then WithOptional
Configure relation: from StudentContact
configuration.
StudentContact
configuration:
HasKey(x => x.Id);
Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasRequired(x => x.Student)
.WithOptional(l => l.Contact)
.Map(x => x.MapKey("Student_Id"));
Student
configuration:
HasKey(x => x.Id);
Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
which would be the same as Option2
.
Option3Mimic: HasRequired then WithOptional
Unable to configure relation from StudentContact
configuration.
Limitations
- Yes, there could be some errors which I haven't faced yet, or could make some ethical disturbance. So if you find any, just let me know.
- There are some issues like navigation properties which I have avoided.
Find Visual Studio 2010 (can be runnable in VS2013) solution of framework 4 project in the attachment. If you run the project, it would create a "UMS
" database with 2 tables and data, at the local machine. You can change it using app.config.