Entity Framework Code First
Pluralsight course Entity Framework 4.1 – Code First by Julie Lerman
General Info
Code first is a very convention heavy method of describing how an application should interact with a database. It is used in place of a .edmx model file and typically implemented when there is no database to begin with, but you may also map the domain classes to a existing data source. Since there is no physical model or XML file to read at start-up the application must create the in-memory metadata directly from the class definitions. Data annotations and fluent API’s are used in addition to the POCO classes to achieve the desired database architecture and functionality.
Conventions
- Primary Key – by default Entity Framework will look for the class property Id or ClassName+Id to use as the primary key when mapping to the database.public int Id { get; set; }public int ClassName+Id { get; set; }
- Foreign Key – EF automatically establishes relationships, when one class has a domain class property, that property is set to the foreign key.
- Virtual Keyword – prefix the domain class property with the virtual keyword to enable lazy loading and have navigation to the related object.public virtual ClassName ClassName { get; set; }
- Connection String – if not specified EF will create the database in SQL Express. Also, until an insert happens, the database will not be generated even if it is seeded.
DbContext: The Meat and Potatoes!
If the domain classes are like tables, then the class that inherits from DbContext is like the database.
- Defining a table – every domain class that is to be persisted in the database must be a property of the context class.public DbSet<ClassName1> ClassName1 { get; set; }public DbSet<ClassName2> ClassName2 { get; set; }//other domain classes…
- Database Initialization – There are three classes in the System.Data.Entity namespace which control how Entity Framework behaves when the DbContext model structure changes.CreateDatabaseIfNotExist<ContextClass> //default strategyDropCreateDatabaseIfModelChanges<ContextClass>DropCreateDatabaseAlways<ContextClass>
- Seeding the database – For each of the database initialization strategies there is an overridable method called Seed which takes a context parameter.protected override void Seed(ContextClass context){//create entities and add them to the databasebase.Seed(context);}
- Fluent API – A more robust way of defining the model’s mapping to the database. Data annotations are more concise, but just a subset of what fluent API’s can do. Fluent API statements are placed in the overridable OnModelCreating method in the DbContext class and take a DbModelBuilder parameter. Some fluent API’s are composable.
- Defining a non-conventional Id property (see Primary Key convention above)modelBuilder.Entity<ClassName>().HasKey(x => x.PropertyName);
- Defining the table name, when table and class don’t matchmodelBuilder.Entity<ClassName>().ToTable("TableName");
- Entity Splitting – Split the domain class into multiple database tables, the tables will share a primary key (must be one-to-one)modelBuilder.Entity<ClassName>().Map(mc =>{mc.Properties(p => new {p.Id, /*other properties for Table 1*/});mc.ToTable("Table1Name");}).Map(mc =>{mc.Properties(p => new {p.Id, /*other properties for Table 2*/});mc.ToTable("Table2Name");});
- Table Splitting – Split the database table into multiple domain classes, first map entities to same table, then configure the bi-directional relationshipmodelBuilder.Entity<PrincipalClassName>().ToTable("SameTableName");
modelBuilder.Entity<ExtendedClassName>().ToTable("SameTableName");
modelBuilder.Entity<PrincipalClassName>().HasRequired(a => a.ExtendedClassName).WithRequiredPrincipal();
- Many-to-Many Relationships – Each class involved will have each other class as one of its properties wrapped in ICollection<>, remember to include the virtual keyword if you want navigation.modelBuilder.Entity<Class1Name>().HasMany(p => p.Class2Name).WithMany(t => t.Class1Name).Map(mc =>{mc.ToTable("Class1JoinClass2");mc.MapLeftKey("Class1Id");mc.MapRightKey("Class2Id");});
- More fluent API’s here.
Data Annotations and Code First
The following is not a comprehensive list of data annotations, just some of the non-trivial, note worthy ones.
- [ConcurrencyCheck] – SQL will look for entries by Id and the property with the [ConcurrencyCheck] attribute when doing updates or deletes. An exception is thrown if nothing come back from the query.
- [Timestamp] – This has the same functionality as a concurrency check attribute but is used on a property of type Byte[] and captures the date and time the entry was modified. Also known as row version.
- [DatabaseGenerated(DatabaseGeneratedOption.Computed)] – Allows a database formula to define the value of the field. Best used with established/existing databases, doesn’t work well with code first.
- [ComplexType] – Attribute at the class level. Complex classes do not have an Id property. When included as a single property in the "parent" class, the complex class’s fields are incorporated into the "parent" class’s table.
- Parent Table
-Field 1
-Field 2
-…
-ComplexClass_Field 1
-ComplexClass_Field 2
-…
- [InverseProperty("PropertyName")] – Defines a bi-directional relationship, when two classes have multiple relationships among themselves.
Inheritance Hierarchy
Table Per Hierarchy – When one domain class inherits from another, Entity Framework assumes this to be a TPH structure. Thus, each derived class is placed in the same database table as the base class and a discriminator field is added along with the other fields from the derived class.
Table Per Type – To configure for this structure, explicitly define what tables to map to (for each member of the hierarchy) via fluent API’s (see above). The result is a TPT structure, and an automatic benefit is that Entity Framework will generate the derived class’ Primary Key and Foreign Key fields to link back to the base class table.