Tuesday, December 10, 2013

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

Configure Domain Classes in Code-First

As you know that you can create entity data model from existing database in database first approach that has all the metadata information in SSDL, CSDL and MSL so that EF can use this model in querying, change tracking, updating functionality etc.. The same way, entity framework code first allows you to use your domain classes to build the model which in-tern will be used by EF in different activity. Code first suggests certain conventions to follow by your domain classes so that EF can understand it and build the model out of it.

However, if your domain classes don’t follow conventions then you also have the ability to configure your domain classes so that EF can understand it and build the model out of it. There are two ways by which you can configure your domain classes:
  1. DataAnnotation
  2. Fluent API

DataAnnotation:

DataAnnotation is simple attribute based configuration which you can apply on your domain classes and its properties. You can find most of the attributes in System.ComponentModel.DataAnnotations namespace. However, DataAnnotation provides only subset of Fluent API configurations. So if you don’t find some attributes in DataAnnotation then you have to use Fluent API to configure it.

Following is an example of DataAnnotation used in Student Class:
     
    [Table("StudentInfo")]
    public class Student
    {
        public Student() { }
        
        [Key]
        public int SID { get; set; }

        [Column("Name", TypeName="ntext")]
        [MaxLength(20)]
        public string StudentName { get; set; }

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

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

Fluent API configuration applied as EF building the model from your domain classes. You can inject the configurations by overriding the DbContext class’s OnModelCreating method as following:
    
    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);
        }
    }
        
You can use modelBuilder which is an object of DbModelBuilder class to configure domain classes.

Let’s see DataAnnotation and Fluent API in detail in the next chapter.

Inheritance Strategy in Code-First

In the code first approach, database schema will be created based on the design of your domain classes. You may design your domain classes in object-oriented way where you can use inheritance and polymorphism. Object-oriented systems model has “has a” and “is a” relationships where as SQL based relational model has only “has a” relationships. So how would you map object-oriented with relational database?

There are three different approaches to represent an inheritance hierarchy:

  • Table per Hierarchy (TPH): This approach suggests one table for entire class inheritance hierarchy. Table includes discriminator column which distinguish between inheritance classes.

  • Table per Type (TPT): This approach suggests one table for each classes thus each class will have its persistence table.

  • Table per Concrete class (TPC): This approach suggests one table for one concrete class but not for abstract class. So if you inherit abstract class in multiple concrete classes then properties of abstract class will be part of each table of concrete class.
We are not going into detail here. Visit following reference link to understand it in detail:



Seed Database in Code-First

You can insert data into your database tables in database initialization process. This will be important if you want to provide some test data for your application or to provide some default master data for your application.

To seed data into your database, you have to create custom DB initializer as you created in the previous chapter and override Seed method. Following example show how you can provide default data for Standard table while initializing the School database:
     
    public class SchoolDBInitializer : DropCreateDatabaseAlways<SchoolDBContext>
    {
        protected override void Seed(SchoolDBContext context)
        {
            IList<Standard> defaultStandards = new List<Standard>();

            defaultStandards.Add(new Standard() { StandardName = "Standard 1", Description = "First Standard" });
            defaultStandards.Add(new Standard() { StandardName = "Standard 2", Description = "Second Standard" });
            defaultStandards.Add(new Standard() { StandardName = "Standard 3", Description = "Third Standard" });

            foreach (Standard std in defaultStandards)
                context.Standards.Add(std);

            //All standards will
            base.Seed(context);
        }
    }