Entity Framework Code First is a method of developing software applications that utilizes the Entity Framework and focuses on the code-first approach. The modeling workflow of Entity Framework Code First involves defining domain classes or models that represent the objects in the application. These models are then used to create a database using the DbContext and DbSets classes.
A database connection is established and managed by the DbContext class, and the database is created based on the models defined in the domain classes. Code First Migrations are used to manage database changes and keep track of the evolution of the database over time.
The mappings between the domain classes and the database are configured using either Data Annotations or Fluent API. Data Annotations are attributes that are added to the models to specify the properties of the database, such as the primary key, data type, etc. Fluent API is a more flexible way of defining mappings that allows developers to use a fluent API syntax instead of data annotations.
Topics covered:
- Modeling Workflow
- Code First Main Parts
- Domain Classes (Models)
- DbContext and DbSets
- Database connection
- Using Code First Migrations
- Configure Mappings
- Data Annotations and Fluent API
Video (in Bulgarian)
Presentation Content
Modeling Workflow
- Entity Framework supports three types of modeling workflow:
- Database first
- Create models as database tables
- Use Management Studio or native SQL queries
- Model first
- Create models using visual EF designer in VS
- Code first
- Write models and combine them in DbContext
- Database first
Why Use Code First?
- Write code without having to define mappings in XML or create database models
- Define objects in POCO
- Reuse these models and their attributes
- No base classes required
- Enables database persistence with no configuration
- Can use automatic migrations
- Can use Data Annotations ( Key , Required , etc.)
Domain Classes (Models)
- Bunch of normal C# classes (POCO)
- May contain navigation properties
public class PostAnswer { public int PostAnswerId { get; set; } public string Content { get; set; } public int PostId { get; set; } public virtual Post Post { get; set; } }
- May contain navigation properties
- Recommended to be in a separate class library
DbContext Class
- A class that inherits from DbContext
- Manages model classes using DbSet type
- Implements identity tracking, change tracking, and API for CRUD operations
- Provides LINQ-based data access
- Recommended to be in a separate class library
- Don’t forget to reference the Entity Framework library (using NuGet package manager)
- If you have a lot of models it is recommended to use more than one DbContext
DbSet Type
- Collection of single entity type
- Set operations: Add , Attach , Remove , Find
- Use with DbContext to query database
public DbSet<Post> Posts { get; set; }
DbContext Example
using System.Data.Entity;
using CodeFirst.Models;
public class ForumContext : DbContext
{
public DbSet<Category> Categories { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<PostAnswer> PostAnswers { get; set; }
public DbSet<Tag> Tags { get; set; }
}
How to Interact With the Data?
In the same way as when we use database first or model first approach
var db = new ForumContext();
var category = new Category { Parent = null, Name = "Database course", };
db.Categories.Add(category);
var post = new Post();
post.Title = "Срока на домашните";
post.Content = "Моля удължете срока на домашните";
post.Type = PostType.Normal;
post.Category = category;
post.Tags.Add(new Tag { Text = "домашни" });
post.Tags.Add(new Tag { Text = "срок" });
db.Posts.Add(post);
db.SaveChanges();
Where is My Data?
- By default app.config file contains link to default connection factory that creates local db
<entityFramework> <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework"> <parameters> <parameter value="v11.0" /> </parameters> </defaultConnectionFactory> </entityFramework> - Server name by default: (localdb)\v11.0 or .\SQLEXPRESS.[full-class-name]
- We can use VS server explorer to view database
How to Connect to SQL Server?
- First, create context constructor that calls base constructor with appropriate connection name
public class ForumContext : DbContext { public ForumContext() : base("ForumDb") { } } - Then add the connection string in app.config _ _
<connectionStrings> <add name="ForumDb" connectionString="Data Source=.;Initial Catalog=ForumDb;Integrated Security=True" providerName="System.Data.SqlClient" /> </connectionStrings>
Changes in Domain Classes
- What happens when we change our models?
- Entity Framework compares our model with the model in __MigrationHistory table
- By default Entity Framework only creates the database and don’t do any changes after that
- Using Code First Migrations we can manage differences between models and database
Code First Migrations
- Enable Code First Migrations
- Open Package Manager Console
- Run Enable-Migrations command
- This will create some initial jumpstart code
- - EnableAutomaticMigrations for auto migrations
- Two types of migrations
- Automatic migrations
- Set AutomaticMigrationsEnabled = true;
- Code-based (providing full control)
- Separate C# code file for every migration
- Automatic migrations
Database Migration Strategies
- CreateDatabaseIfNotExists (default)
- DropCreateDatabaseIfModelChanges
- We loose all the data when change the model
- DropCreateDatabaseAlways
- Great for automated integration testing
- MigrateDatabaseToLatestVersion
- This option uses our migrations
- We can implement IDatabaseInitializer if we want custom migration strategy
Use Code First Migrations
- First, enable code first migrations
- Second, we need to tell to Entity Framework to use our migrations with code (or app.config)
Database.SetInitializer( new MigrateDatabaseToLatestVersion <ForumContext, Configuration>()); - We can configure automatic migration
public Configuration() { this.AutomaticMigrationsEnabled = true; this.AutomaticMigrationDataLossAllowed = true; }
Seeding the Database
- During a migration we can seed the database with some data using the Seed method
protected override void Seed(ForumContext context) { /* This method will be called after migrating to the latest version. You can use the DbSet<T>.AddOrUpdate() helper extension method to avoid creating duplicate seed data. E.g. */ context.Tags.AddOrUpdate(new Tag { Text = "срок" }); context.Tags.AddOrUpdate(new Tag { Text = "форум" }); } - This method will be run every time (since EF 5)
Configure Mappings
- Entity Framework respects mapping details from two sources
- Data annotation attributes in the models
- Can be reused for validation purposes
- Fluent API code mapping configuration
- By overriding OnModelCreating method
- By using custom configuration classes
- Data annotation attributes in the models
- Use one approach or the other
Data Annotations
- There is a bunch of data annotation attributes in System.ComponentModel.DataAnnotations
- [Key] – specifies the primary key of the table
- For validation: [ StringLength ] , [ MaxLength ] , [ MinLength ] , [Required]
- Schema: [Column] , [Table] , [ ComplexType ] , [ ConcurrencyCheck ] , [Timestamp] , [ ComplexType ] , [ InverseProperty ] , [ ForeignKey ] , [ DatabaseGenerated ] , [ NotMapped ]
- In EF 6 we will be able to add custom attributes by using custom conventions
Fluent API for Mappings
- By overriding OnModelCreating method in DbContext class we can specify mapping configurations
protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Tag>().HasKey(x => x.TagId); modelBuilder.Entity<Tag>().Property(x => x.Text).IsUnicode(true); modelBuilder.Entity<Tag>().Property(x => x.Text).HasMaxLength(255); // modelBuilder.Entity<Tag>().Property(x => x.Text).IsFixedLength(); base.OnModelCreating(modelBuilder); }
Fluent API Configurations
- .Entity()
- Map: Table Name, Schema
- Inheritance Hierarchies, Complex Types
- Entity -> Multiple Tables
- Table -> Multiple Entities
- Specify Key (including Composite Keys)
- .Property()
- Attributes (and Validation)
- Map: Column Name, Type, Order
- Relationships
- Concurrency
Homework
- Using c0de first approach, create database for student system with the following tables:
- Students (with Id, Name, Number, etc.)
- Courses (Name, Description, Materials, etc.)
- StudentsInCourses (many-to-many relationship)
- Homework (one-to-many relationship with students and courses), fields: Content, TimeSent
- Annotate the data models with the appropriate attributes and enable code first migrations
- Write a console application that uses the data
- Seed the data with random values