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