Previous article we explain how to use Fluent API to configure / map attributes and types, this article will focus on how it is configured relationships.
As used herein the following code
public class Student { public int ID { get; set; } public string Name { get; set; } public DateTime EnrollmentDate { get; set; } // Navigation properties public virtual Address Address { get; set; } public virtual OtherInfo OtherInfo { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } } public class Department { public Department() { this.Courses = new HashSet<Course>(); } // Primary key public int DepartmentID { get; set; } public string Name { get; set; } public decimal Budget { get; set; } public System.DateTime StartDate { get; set; } public int? Administrator { get; set; } // Navigation property public virtual ICollection<Course> Courses { get; private set; } } public class Course { public int CourseID { get; set; } public string Title { get; set; } public int Credits { get; set; } // Foreign key public int DepartmentID { get; set; } public string DepartmentName { get; set; } public int SomeDepartmentID { get; set; } // Navigation properties public virtual Department Department { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } public virtual ICollection<Instructor> Instructors { get; set; } } public class Instructor { public int InstructorID { get; set; } public string Name { get; set; } public DateTime HireDate { get; set; } // Navigation properties public virtual ICollection<Course> Courses { get; set; } } public class Enrollment { public int EnrollmentID { get; set; } public int CourseID { get; set; } public int StudentID { get; set; } // Navigation property public virtual Course Course { get; set; } public virtual Student Student { get; set; } } public class Address { public int AddressId { get; set; } public string HomeAddress { get; set; } public string LiveAddress { get; set; } // Navigation property public virtual Student Student { get; set; } } public class OtherInfo { public int Id { get; set; } public string HomeAddress { get; set; } public string MailAddress { get; set; } public string PhoneNumber { get; set; } public string StudentID { get; set; } // Navigation property public virtual Student Student { get; set; } }
EntityTypeConfiguration<TEntityType>
The above method is part of a generic class of shots, we generally by Code First Fluent API instance is to configure the relationship between entities is beginning from a generic class, this class provides a generic approach for the majority of our relationship, such as must HasRequired , optional HasOptional, more hasMany.
All of the above methods (except hasMany, HasOptional, HasRequired ) is the return value of the generic class EntityTypeConfiguration <TEntityType> Examples of itself, to which the chain provides great convenience to operation.
HasRequired, HasOptional, hasMany these three parameters is a method of the lambda expression The , except for the first two entities representing the relationship ( Relationship navigation properties (inter) Navigation Property ), the latter is a collection of navigation attributes ( Collection )
HasRequired, HasOptional, hasMany return values of these three methods is to continue to be a generic class instance configuration, although the names are different, the method name is slightly different, but the main method embodied idea is the same
RequiredNavigationPropertyConfiguration<TEntityType, TTargetEntityType>
OptionalNavigationPropertyConfiguration<TEntityType, TTargetEntityType>
ManyNavigationPropertyConfiguration<TEntityType, TTargetEntityType>
Three generic classes have the following three methods analogous WithRequired, WithOptional, WithMany , these three methods are no arguments provided overloaded version, there is also reference a process parameter the lambda expression The , for the first two represent an entity relationship ( relationship navigation properties (inter) navigation property ), the latter is a collection of navigation attributes ( collection )
Next, you can use HasForeignKey continue to configure the foreign key attribute, a parameter method is still the lambda expression The , but on behalf of a property
DependentNavigationPropertyConfiguration<TDependentEntityType>
public CascadableNavigationPropertyConfiguration HasForeignKey<TKey>(Expression<Func<TDependentEntityType, TKey>> foreignKeyExpression);
Other methods comprise two major classes the Map , the following
ForeignKeyNavigationPropertyConfiguration
public CascadableNavigationPropertyConfiguration Map(Action<ForeignKeyAssociationMappingConfiguration> configurationAction);
ManyToManyNavigationPropertyConfiguration<TEntityType, TTargetEntityType>
public ManyToManyNavigationPropertyConfiguration<TEntityType, TTargetEntityType> Map(Action<ManyToManyAssociationMappingConfiguration> configurationAction); public ManyToManyNavigationPropertyConfiguration<TEntityType, TTargetEntityType> MapToStoredProcedures(); public ManyToManyNavigationPropertyConfiguration<TEntityType, TTargetEntityType> MapToStoredProcedures(Action<ManyToManyModificationStoredProceduresConfiguration<TEntityType, TTargetEntityType>> modificationStoredProcedureMappingConfigurationAction);
We can continue to be configured on the instances returned
CascadableNavigationPropertyConfiguration
public void WillCascadeOnDelete(); public void WillCascadeOnDelete(bool value);
We see the cascading deletes
Configuring Relationships
1:1,0 - Configuring a Required-to-Optional Relationship (One-to–Zero-or-One)
A student can have one or no other information (including mailing address, phone number, etc.)
// Map one-to-zero or one relationship modelBuilder.Entity<OtherInfo>() .HasRequired(t => t.Student) .WithOptional(t => t.OtherInfo);
1:1 - Configuring a Relationship Where Both Ends Are Required (One-to-One)
A student must have an address information (including home address, residential address)
// Map one-to-one relationship modelBuilder.Entity<Address>() .HasRequired(t => t.Student) .WithRequiredPrincipal(t => t.Address);
1:N - Configuring a Required-to-Many Relationship (One-to-Many)
A student can take many courses
// Map one-to-many relationship modelBuilder.Entity<Student>() .HasMany(t => t.Enrollments) .WithRequired(t => t.Student);
N:N - Configuring a Many-to-Many Relationship (Many-to-Many)
A teacher can teach many courses, a course can also be taught by several teachers
// Map one-to-many relationship modelBuilder.Entity<Course>() .HasMany(t => t.Instructors) .WithMany(t => t.Courses);
You can further specify intermediate join table (database would create an intermediate connection table)
// Map one-to-many relationship modelBuilder.Entity<Course>() .HasMany(t => t.Instructors) .WithMany(t => t.Courses) .Map(m => { m.ToTable("CourseInstructor"); m.MapLeftKey("CourseID"); m.MapRightKey("InstructorID"); });
Configuring a Relationship with One Navigation Property - Configuration relations based navigation properties
If the relationship is unidirectional, i.e. only two entities in one entity definition navigation attributes. Code First Conventions can automatically infer this relationship is one-to-many.
For example, you want to Student and Address established between the two entities one-to-one relationship, and only in the Address contains navigation properties on entity, then you will need to use Code First Fluent API configuration relationship
// Map one-to-one relationship modelBuilder.Entity<Address>() .HasRequired(t => t.Student) .WithRequiredPrincipal();
WillCascadeOnDelete - Enabling Cascade Delete (cascade delete)
You can use WillCascadeOnDelete to cascade delete relationship, if the foreign key on the slave body is not Nullable , then the Code First will set up cascading deletes, or they will not set up cascading deletes, but rather only the foreign key set to null
In the Code First Conventions under is removed cascading deletes
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>() modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>()
Code First Fluent API is as follows
// Cascade Delete modelBuilder.Entity<Course>() .HasRequired(t => t.Department) .WithMany(t => t.Courses) .HasForeignKey(d => d.DepartmentID) .WillCascadeOnDelete(false);
Configuring a Composite Foreign Key - foreign key configuration combinations
If Department primary key for the primary key combination DepartmentID, Name, it can Fluent API is Course given combination foreign key
// Composite primary key modelBuilder.Entity<Department>() .HasKey(d => new { d.DepartmentID, d.Name }); // Composite foreign key modelBuilder.Entity<Course>() .HasRequired(c => c.Department) .WithMany(d => d.Courses) .HasForeignKey(d => new { d.DepartmentID, d.DepartmentName });
Renaming a Foreign Key That Is Not Defined in the Model - foreign key rename
You can rename the foreign key name
// Renaming a Foreign Key That Is Not Defined in the Model modelBuilder.Entity<Course>() .HasRequired(c => c.Department) .WithMany(t => t.Courses) .Map(m => m.MapKey("ChangedDepartmentID"));
Configuring a Foreign Key Name That Does Not Follow the Code First Convention
If the foreign key attribute names on the slave entity does not meet Code First Conventions specification, meaning that there is no slave entity foreign keys, you can specify the following manner
// Configuring a Foreign Key Name That Does Not Follow the Code First Convention modelBuilder.Entity<Course>() .HasRequired(c => c.Department) .WithMany(d => d.Courses) .HasForeignKey(c => c.SomeDepartmentID);
Original link: http://msdn.microsoft.com/en-us/data/jj591620