Code First Approach

Web page by Kevin Harris of Homer IL

Please contact Kevin Harris of Homer IL concerning this web site

"Code First generates a database based on data classes created by the developer."


Code First Approach


Entity Framework evolved quickly supporting only the database-first approach in its first release (EF or EF3.5), then model-first in EF4, and code-first in EF4.1. Database-first and code-first support regeneration when either the database schema changes (DF), or the data classes change (CF), making them more popular the model-first which supports only a single generation. Database-first generates an Entity Data Model in an XML file (.edmx) which can be updated as the database changes. Code-first supports database migrations to keep the database up to date with changes to the data classes.

The following files are used in the code-first approach. They include the data classes, with data annotations for indicating the data characteristics used when creating the database. Also the Web.config containing the connection string to the database, the Global.asax.cs with instructions on how to create the database. A database initializer for seeding the database and the context class (derived from DbContext) which provides the code with a gateway to the database data.

After theses files is a set of instructions for setting up a code-first project. Microsoft also provides sites describing how to creating code-first databases, such asCode First to a New Database which s a video and step-by-step walkt hrough providing an introduction to Code First development targeting a new database.


Code-First Example FIles


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace CathyStore.Web.Models
{
    public class Category
    {
        [ScaffoldColumn(false)]
        public int CategoryID { get; set; }

        [Required, StringLength(100), Display(Name ="Name")]
        public string CategoryName { get; set; }

        [Display(Name = "Product Description")]
        public string Description { get; set; }

        public virtual ICollection<Product> Products { get; set; }
    }
} 

Data Class with Data Annotations

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
using System.Data.Entity;

namespace CathyStore.Web.Models
{
    public class ProductContext : DbContext    
    {
        public ProductContext() : base("CathyStore")
        {
        }
        public DbSet<Category> Categories { get; set; }
        public DbSet<Product> Products { get; set; }
        public DbSet<CartItem> CartItems { get; set; }
        public DbSet<Order> Orders { get; set; }
        public DbSet<OrderDetail> OrderDetails { get; set; }
    }
}

ProductContext.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using System.Collections.Generic;
using System.Data.Entity;

namespace CathyStore.Web.Models
{
    //public class ProductDatabaseInitializer : DropCreateDatabaseAlways<ProductContext>
    public class ProductDatabaseInitializer : DropCreateDatabaseIfModelChanges<ProductContext>
    {
        protected override void Seed(ProductContext context)
        {
            GetCategories().ForEach(c => context.Categories.Add(c));
            GetProducts().ForEach(p => context.Products.Add(p));
        }

        private static List<Category> GetCategories()
        {
            var categories = new List<Category> { new Category { CategoryID = 1, CategoryName = "Cars" }, new Category { CategoryID = 2, CategoryName = "Planes" }, new Category
{ CategoryID = 3, CategoryName = "Trucks" }, new Category { CategoryID = 4, CategoryName = "Boats" }, new Category { CategoryID = 5, CategoryName = "Rockets" }, }; return categories;
        }

        private static List<Product> GetProducts()
        {
            var products = new List<Product> { new Product { ProductID = 1, ProductName = "Convertible Car", Description = "This convertible car is fast! The engine is powered by a neutrino based battery (not included)." + "Power it up and let it go!", ImagePath="carconvert.png", UnitPrice = 22.50, CategoryID = 1 }, new Product { ProductID = 2, ProductName = "Old-time Car", Description = "There's nothing old about this toy car, except it's looks. Compatible with other old toy cars.", ImagePath="carearly.png", UnitPrice = 15.95, CategoryID = 1 }, new Product { ProductID = 3, ProductName = "Fast Car", Description = "Yes this car is fast, but it also floats in water.", ImagePath="carfast.png", UnitPrice = 32.99, CategoryID = 1 }, new Product { ProductID = 4, ProductName = "Super Fast Car", Description = "Use this super fast car to entertain guests. Lights and doors work!", ImagePath="carfaster.png", UnitPrice = 8.95, CategoryID = 1 },
new Product { ProductID = 5, ProductName = "Old Style Racer", Description = "This old style racer can fly (with user assistance). Gravity controls flight duration." + "No batteries required.", ImagePath="carracer.png", UnitPrice = 34.95, CategoryID = 1 }, new Product { ProductID = 6, ProductName = "Ace Plane", Description = "Authentic airplane toy. Features realistic color and details.", ImagePath="planeace.png", UnitPrice = 95.00, CategoryID = 2 }, new Product { ProductID = 7, ProductName = "Glider", Description = "This fun glider is made from real balsa wood. Some assembly required.", ImagePath="planeglider.png", UnitPrice = 4.95, CategoryID = 2 }, new Product { ProductID = 8, ProductName = "Paper Plane", Description = "This paper plane is like no other paper plane. Some folding required.", ImagePath="planepaper.png", UnitPrice = 2.95, CategoryID = 2 }, new Product { ProductID = 9, ProductName = "Propeller Plane", Description = "Rubber band powered plane features two wheels.", ImagePath="planeprop.png", UnitPrice = 32.95, CategoryID = 2 }, new Product { ProductID = 10, ProductName = "Early Truck", Description = "This toy truck has a real gas powered engine. Requires regular tune ups.", ImagePath="truckearly.png", UnitPrice = 15.00, CategoryID = 3 }, new Product {
ProductID = 11, ProductName = "Fire Truck", Description = "You will have endless fun with this one quarter sized fire truck.", ImagePath="truckfire.png", UnitPrice = 26.00, CategoryID = 3 }, new Product { ProductID = 12, ProductName = "Big Truck", Description = "This fun toy truck can be used to tow other trucks that are not as big.", ImagePath="truckbig.png", UnitPrice = 29.00, CategoryID = 3 }, new Product { ProductID = 13, ProductName = "Big Ship", Description = "Is it a boat or a ship. Let this floating vehicle decide by using its " + "artifically intelligent computer brain!", ImagePath="boatbig.png", UnitPrice = 95.00, CategoryID = 4 }, new Product { ProductID = 14, ProductName = "Paper Boat", Description = "Floating fun for all! This toy boat can be assembled in seconds. Floats for minutes!" + "Some folding required.", ImagePath="boatpaper.png", UnitPrice = 4.95, CategoryID = 4 }, new Product { ProductID = 15, ProductName = "Sail Boat", Description = "Put this fun toy sail boat in the water and let it go!", ImagePath="boatsail.png", UnitPrice = 42.95, CategoryID = 4 }, new Product { ProductID = 16, ProductName = "Rocket", Description = "This fun rocket will travel up to a height of 200 feet.", ImagePath="rocket.png", UnitPrice = 122.95, CategoryID = 5 } }; return products;

        }
    }
}

ProductDatabaseInitializer.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using CathyStore.Web.Models;
using System;
using System.Data.Entity;
using System.Net;
using System.Web;
using System.Web.Optimization;
using System.Web.Routing;

namespace CathyStore.Web
{
    public class Global : HttpApplication
    {
        void Application_Start(object sender, EventArgs e)
        {
            // Paypal started using TLS 1.2 in early 2016
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            // Code that runs on application startup
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            // Initialize the database
            Database.SetInitializer(new ProductDatabaseInitializer());
        }
    }
}

Global.asax.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
  <configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework"
      type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
      requirePermission="false"/>
  </configSections>
  <connectionStrings>
    <!--<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\aspnet-CathyStore.Web-20160621074659.mdf;Initial Catalog=aspnet-CathyStore.Web-20160621074659;Integrated Security=True"
      providerName="System.Data.SqlClient" />-->

    <add name="DefaultConnection" connectionString="Data Source=(localdb)\mssqllocaldb;Initial Catalog=aspnet-CathyStore;Integrated Security=True" providerName="System.Data.SqlClient" />
    
    <add name="CathyStore" connectionString="Data Source=(localdb)\mssqllocaldb;
         AttachDbFilename=|DataDirectory|\cathystore.mdf;Integrated Security=True" 
         providerName="System.Data.SqlClient" />    
    
  </connectionStrings>

Web.config


Creating a Code-First Database Project


1. Create new project containing a class library for the data classes. Use the "Class Library" template under "Visual C#". Caution: do not use class library under "Web". Do not use "Class Library(Package)". Inside data class library, create data classes and any associated enums for the data domain.

.Data Class Library in Visual Studio 2015 (.NET 4.5.2)

Data Class Library in Visual Studio 2015 (.NET 4.5.2)


2. Create another class library project called "DataModel" for the Entity Framework. Once the library is created, use NuGet to add in Entity Framework (I used EF 6.1.3). During the installation of the EF package, an "App.config" file is created in the project with default values for MS SQL providers. I am using MySQL as a data provider in this example.

The DataModel created a "Class1.cs" file which was changed to the context class called "CodeFirstContext.cs". Inside the context class define the Dbset classes for each data class. The DbSet class acts as a repository. This is the same as the repository used when coding the Repository Pattern. The queries are performed against the DbSet objects.

Notes:
1. Be sure to add a reference from the DataModel project to the DataClasses project.
2. Be sure all the data classes are public. They are created without a protection level, which defaults to private in C#.

Once the context class is created, you can validate the model, to get an advanced look at how EF will generate the database, by using Entity Framework Power Tools. The Entity Framework Extensions has not been defined for Visual Studio 2015 at this writing, so it has to be downloaded from the Visual Studio Gallery and installed. After the download completes rename the "EFPowerTools.vsix" file to have a ".zip" extension and extract. In the "extension.vsixmanifest" file, find the Supported Production sections and copy/paste to create a new entry for Visual Studio Version = 15. Zip the files in the extracted folder back up with a new name. Rename the zip file to a "vsix" extension and then double-click to run the vsix installer. Once the vsix file is installed, close VS 2105, reopen and from Tools, Extensions and Updates, locate and run the Entity Framework Power Tools install.

With Entity Framework Power Tools installed, be sure the Data Model is the "Start Up Project". Then in the Solution Explorer, select the context class, right-click, select "Entity Framework", then "View Entity Data Model (Read Only)". This will infer the model from the DbContext creating the .edmx file. You may need to make changes to the classes so EF infers the model correctly, especially with foreign key relationships. This may include using data annotations to mark keys as required or including a "virtual" type for the referenced class in the parent class. These changes can also be accomplished using the "Fluent API".

3. Migrate database to create or update database.

Database Migration is the process of creating the initial database or make changes to the database to match changes to the model.

To create initial database, in Package Manager Console window (which is really just a wrapper around powershell), type:

A. Enable Migrations

PM> enable-migrations

B. Add migrations file for creating the database, give it a name.

PM> add-migration 'Initial'

C. Apply migration to create an SQL script that would create the database.

PM> update-database - script

D. Apply migration to create database. Use verbose parameter to display processing information.

PM> update-database - verbose