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

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; }
      }
      
  • 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

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
  • 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

  1. 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
  2. Write a console application that uses the data
  3. Seed the data with random values