Tuesday, December 10, 2013

Configure Many-to-Many relationship

We are going to configure Many-to-Many relationship between Student and Course entity classes.

Configure Many-to-Many relationship using DataAnnotation:

Student class should have collection navigation property for Course and Course should have collection navigation property for student which will create Many-to-Many relationship between student and course as below:
     
    public class Student
    {
        public Student() { }

        public int StudentId { get; set; }
        [Required]
        public string StudentName { get; set; }

        public int StdandardId { get; set; }
        
        public virtual ICollection<Course> Courses { get; set; }
    }
        
    
    public class Course
    {
        public Course()
        {
            this.Students = new HashSet<Student>();
        }

        public int CourseId { get; set; }
        public string CourseName { get; set; }

        public virtual ICollection<Student> Students { get; set; }
    }
        
Above code will create following database where code first will create third joining table CourseStudent which will consist PK of both the tables ie. StudentId & CourseId:

one-to-one relationship in code first

Configure Many-to-Many relationship using Fluent API:

You can use Fluent API to configure Many-to-Many relationship between Student and Course as following:
   
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {

       modelBuilder.Entity<Student>().HasMany<Course>(s => s.Courses).WithMany(c => c.Students).Map(c =>
        {
            c.MapLeftKey("Student_id");
            c.MapRightKey("Course_id");
            c.ToTable("StudentAndCourse");
        });

        base.OnModelCreating(modelBuilder);
    }
        
As you can see in above code that we are mapping left key (key column of Student class)with “Student_id” and right key (key column of Course class) with “Course_id” of table "StudentAndCourse".

This will create new joining table “StudentAndCourse” with two PK which is also FK as below:

one-to-one relationship in code first

Configure One-to-Many Relationship

We are going to configure One-to-Many relationship between Student and Standard as many students are studying in one standard.

Configure One-to-Many relationship using DataAnnotation:

Student entity class has reference property of Standard class with StandardId foreignKey proeprty and Standard class has collection property for Students. So this DataAnnotation will result in One-to-Many relationship.
     
    public class Student
    {
        public Student() { }

        public int StudentId { get; set; }
        [Required]
        public string StudentName { get; set; }

         public int StdandardId { get; set; }
        
        public virtual Standard Standard { get; set; }
    }
        
    
    public class Standard
    {
        public Standard()
        {
            Students = new List<Student>();
        }
        public int StandardId { get; set; }
        public string StandardName { get; set; }
        public string Description { get; set; }

        public virtual ICollection<Student> Students { get; set; }
    }
        
Configure One-to-Many relationship using Fluent API:

Suppose your Student and Standard entity class doesn’t follow code first conventions and have different property names, for example:
   
    public class Student
    {
        public Student(){ }

        public int StudentId { get; set; }
        [Required]
        public string StudentName { get; set; }

        //StdId is not following code first conventions name
        public int StdId { get; set; }

        public virtual Standard Standard { get; set; }
    }
        
    
    public class Standard
    {
        public Standard()
        {
            StudentsList = new List<Student>();
        }
        public int StandardId { get; set; }
        public string StandardName { get; set; }
        public string Description { get; set; }

        public virtual ICollection<Student> StudentsList { get; set; }
    }
        
So you can use Fluent API to configure One-to-Many relationship between Student and Standard entity classes:
    
   protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
            //one-to-many 
            modelBuilder.Entity<Student>().HasRequired<Standard>(s => s.Standard)
            .WithMany(s => s.StudentsList).HasForeignKey(s => s.StdId);

    }
        
Other possible way:
    
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
            //one-to-many
            modelBuilder.Entity<Standard>().HasMany<Student>(s => s.StudentsList)
            .WithRequired(s => s.Standard).HasForeignKey(s => s.StdId);
    }
        
Above code will create following database:
one-to-one relationship in code first

Entity Data Model in designer will look like below:
one-to-one relationship in code first


Download Sample Project:

Download Entity Framework Sample Project

Configure One-to-One Relationship

We are going to configure One-to-One relationship between Student and StudentAddress. As you may know that one to one relationship happens when primary key of one table becomes PK & FK in another table. Here, StudentId is a Primary key of Student table so StudentId should be PK and FK in StudentAddress table in order to have one to one (one to zero or one) relationship between them.

Configure one to zero or one relationship using DataAnnotation:
     
    public class Student
    {
        public Student() { }

        public int StudentId { get; set; }
        [Required]
        public string StudentName { get; set; }

        [Required]
        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; }
    }
        
As you can see in above Student and StudentAddress class, we haven’t done anything special in Student class because StudentId follows the conventions so it will become PK. Now, we use Key and ForeignKey attribute for StudentId in StudentAddress class to mark it as PK as well as FK. So thus it will create one-to-one relationship between Student and StudentAddress.

Configure One-to-One relationship using Fluent API:
   
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<StudentAddress>()
            .HasKey(e => e.StudentId);
        modelBuilder.Entity<StudentAddress>()
                    .Property(e => e.StudentId)
                    .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
        modelBuilder.Entity<StudentAddress>()
                    .HasRequired(e => e.Student)
                    .WithRequiredDependent(s => s.StudentAddress);

        base.OnModelCreating(modelBuilder);
    }
        
Above code will create following database:

one-to-one relationship in code first

You can check the relationship between Student and StudentAddress in the database as below:

one-to-one relationship in code first

If you create entity data model of created database then it will look like this:

one-to-one relationship in code first

You will learn how to create one-to-many relationship in the next chapter.

Download Sample Project:

Download Entity Framework Sample Project

Fluent API Class Hierarchy

Below image shows hierarchy of important Fluent API classes:

fluent api

As you can see that I have separated classes by levels eg. DbModelBuilder is level 0 class because it’s main class in Fluent API configuration. EntityTypeConfiguration is Level 1 class because we can set relationship between entities using this class. ManyNavigarionConfiguration, OptionalNavigationPropertyConfiguration and RequiredNavigationPropertyConfiguration are Level 2 classes which set some additional configuration between entities.

So, Level 1 and Level 2 classes can be used to configure relationship between the entities that will be mapped to database tables. Level 3 & 4 can be used to configure additional mapping between the entities.

Next chapters will explain how to use these classes to configure One-to-One, One-to-Many and Many-to-Many relationships using these classes.

EntityTypeConfiguration Class in Code-First

It is important to understand EntityTypeConfiguration class because it is an important class that allows configuration to be performed for an entity type in a model. It can be obtained by calling Entity method of DbModelBuilder class:

EntityTypeConfiguration

EntityTypeConfiguration has following important methods:

Method NameReturn TypeDescription
HasKey<TKey>EntityTypeConfigurationConfigures the primary key property(s) for this entity type.
HasMany<TTargetEntity>ManyNavigationPropertyConfigurationConfigures a many relationship from this entity type.
HasOptional<TTargetEntity>OptionalNavigationPropertyConfigurationConfigures an optional relationship from this entity type. Instances of the entity type will be able to be saved to the database without this relationship being specified. The foreign key in the database will be nullable.
HasRequired<TTargetEntity>RequiredNavigationPropertyConfigurationConfigures a required relationship from this entity type. Instances of the entity type will not be able to be saved to the database unless this relationship is specified. The foreign key in the database will be non-nullable.
Ignore<TProperty>VoidExcludes a property from the model so that it will not be mapped to the database.
MapEntityTypeConfigurationAllows advanced configuration related to how this entity type is mapped to the database schema.
Property<T>StructuralTypeConfigurationConfigures a struct property that is defined on this type.
ToTableVoidConfigures the table name that this entity type is mapped to.

Visit MSDN for more information on EntityTypeConfiguration class.

Fluent API in Code-First

As you have seen in previous chapter that you can configure your domain classes by overriding OnModelCreating method of DBContext in your context class. For example:
    
    public class SchoolDBContext: DbContext 
    {
        public SchoolDBContext(): base("SchoolDBConnectionString") 
        {
        }

        public DbSet<Student> Students { get; set; }
        public DbSet<Standard> Standards { get; set; }
        public DbSet<StudentAddress> StudentAddress { get; set; }
        
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            //Configure domain classes using Fluent API here

            base.OnModelCreating(modelBuilder);
        }
    }
        

DbModelBuilder is main class by which you can configure domain classes. Configuration done by using the DbModelBuilder API takes precedence over data annotations which in turn take precedence over the default conventions.

DbModelBuilder class has following important property and method:
Property / Method NameDescriptionReturn Type
ConventionsProvides access to the settings of this DbModelBuilder that deal with conventions. You can disable the conventions for the DbModelBuilder. 
Entity<TEntityType>()Registers an entity type as part of the model and returns an object that can be used to configure the entity. This method can be called multiple times for the same entity to perform multiple lines of configuration.EntityTypeConfiguration<TEntityType>

Visit MSDN for more information on DbModelBulder class.

DataAnnotation in Code-First

EF Code first provides set of DataAnnotation attributes which you can apply on your domain classes and properties. You have to include System.ComponentModel.DataAnnotations namespace to use DataAnnotation attributes. DataAnnotation basically includes attributes for server side validations and database related attributes.

Validation Attributes: 

Annotation AttributeDescription
RequiredThe Required annotation will force EF (and MVC) to ensure that property has data in it.
MinLengthMinLength annotation validates property whether it has minimum length of array or string.
MaxLengthMaxLength annotation maximum length of property which in-tern set maximum length of column in the database
StringLengthSpecifies the minimum and maximum length of characters that are allowed in a data field.

Database Schema related Attributes:

Annotation AttributeDescription
TableSpecify name of the DB table which will be mapped with the class
ColumnSpecify column name and datatype which will be mapped with the property
KeyMark property as EntityKey which will be mapped to PK of related table.
ComplexTypeMark the class as complex type in EF.
TimestampMark the property as a non-nullable timestamp column in the database.
ForeignKeySpecify Foreign key property for Navigation property
NotMappedSpecify that property will not be mapped with database
ConcurrencyCheckConcurrencyCheck annotation allows you to flag one or more properties to be used for concurrency checking in the database when a user edits or deletes an entity.
DatabaseGeneratedDatabaseGenerated attribute specifies that property will be mapped to Computed column of the database table. So the property will be read-only property. It can also be used to map the property to identity column (auto incremental column).
InversePropertyInverseProperty is useful when you have multiple relationship between two classes.

DataAnnotation example:
    
    [Table("StudentInfo")]
    public class Student
    {
        public Student(){ }
        
        [Key]
        public int SID { get; set; }

        [Required(ErrorMessage="Student Name is Required" )]
        [Column("Name", TypeName="ntext")]        
        [MaxLength(20), MinLength(2, ErrorMessage="Student name can not be 2 character or less")]
        public string StudentName { get; set; }

        [NotMapped]
        public int? Age { get; set; }

        [ConcurrencyCheck()]
        [Timestamp]
        public Byte[] LastModifiedTimestamp { get; set; }

        public int? MathScore { get; set; }
        
        public int? ScienceScore { get; set; }


        [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
        public int? TotalScore
        {
            get;
            set;

        }
        
        public int StdId { get; set; }

        [ForeignKey("StdId")]
        public virtual Standard Standard { get; set; }
    }
        

Download DataAnnotation Sample project:


Download Entity Framework Code-First Sample Project