Tuesday, 1 January 2019

First EF Core Console Application

Here you will learn how to use Entity Framework Core with Code-First approach step by step. To demonstrate this, we will create a .NET Core Console application using Visual Studio 17 (or greater).
The .NET Core Console application can be created either using Visual Studio 2017 or Command Line Interface (CLI) for .NET Core. Here we will use Visual Studio 2017.
To create .NET Core Console application, open Visual Studio 2017 and select on the menu: File -> New -> Project.. This will open the New Project popup, as shown below.
In the New Project popup, expand Installed -> Visual C# in the left pane and select the Console App (.NET Core) template in the middle pane. Enter the Project Name & Location and click the OK button to create a console application, as shown below.
Now, we need to install EF Core in our console application using Package Manager Console. Select on the menu: Tools -> NuGet Package Manager -> Package Manager Console and execute the following command to install the SQL Server provider package:
PM> Install-Package Microsoft.EntityFrameworkCore.SqlServer
Learn more about installing EF Core in the EF Core Installation chapter.

Creating the Model

Entity Framework needs to have a model (Entity Data Model) to communicate with the underlying database. It builds a model based on the shape of your domain classes, the Data Annotations and Fluent API configurations.
The EF model includes three parts: conceptual model, storage model, and mapping between the conceptual and storage models. In the code-first approach, EF builds the conceptual model based on your domain classes (entity classes), the context class and configurations. EF Core builds the storage model and mappings based on the provider you use. For example, the storage model will be different for the SQL Server compared with DB2.
EF uses this model for CRUD (Create, Read, Update, Delete) operations to the underlying database.
So, we need to create entity classes and context classes first. The followings are simple entity classes for Student and Course:
public class Student
{
    public int StudentId { get; set; }
    public string Name { get; set; }
}

public class Course
{
    public int CourseId { get; set; }
    public string CourseName { get; set; }
}
Now, we need to create a context class by deriving the DbContext, as shown in the previous chapter. The following SchoolContext class is also called context class.
namespace EFCoreTutorials
{
    public class SchoolContext : DbContext
    {
        public DbSet<Student> Students { get; set; }
        public DbSet<Course> Courses { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        { 
            optionsBuilder.UseSqlServer(@"Server=.\SQLEXPRESS;Database=SchoolDB;Trusted_Connection=True;");
        }
    }
}
The above context class includes two DbSet<TEntity> properties, for Student and Course, type which will be mapped to the Students and Courses tables in the underlying database. In the OnConfiguring() method, an instance of DbContextOptionsBuilder is used to specify which database to use. We have installed MS SQL Server provider, which has added the extension method UseSqlServer on DbContextOptionsBuilder.
The connection string "Server=.\SQLEXPRESS;Database=SchoolDB;Trusted_Connection=True;" in the UseSqlServer method provides database information: Server= specifies the DB Server to use, Database= specifies the name of the database to create and Trusted_Connection=True specifies the Windows authentication mode. EF Core will use this connection string to create a database when we run the migration.
After creating the context and entity classes, it's time to add the migration to create a database.

Adding a Migration

EF Core includes different migration commands to create or update the database based on the model. At this point, there is no SchoolDB database. So, we need to create the database from the model (entities and context) by adding a migration.
We can execute the migration command using NuGet Package Manger Console as well as dotnet CLI (command line interface).
In Visual Studio, open NuGet Package Manager Console from Tools -> NuGet Package Manager -> Package Manager Console and enter the following command:
PM> add-migration CreateSchoolDB
If you use dotnet CLI, enter the following command.
> dotnet ef migrations add CreateSchoolDB
This will create a new folder named Migrations in the project and create the ModelSnapshot files, as shown below.
Learn more about it in the Migration chapter.
After creating a migration, we still need to create the database using the update-database command in the Package Manager Console, as below.
PM> update-database –verbose
Enter the following command in dotnet CLI.
> dotnet ef database update
This will create the database with the name and location specified in the connection string in the UseSqlServer() method. It creates a table for each DbSet property (Students and Courses) as shown below.
This was the first migration to create a database. Now, whenever we add or update domain classes or configurations, we need to sync the database with the model using add-migration and update-database commands.

Reading or Writing Data

Now, we can use the context class to save and retrieve data, as shown below.
namespace EFCoreTutorials
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var context = new SchoolContext()) {

                var std = new Student()
                {
                     Name = "Bill"
                };

                context.Students.Add(std);
                context.SaveChanges();
            }
        }
    }
}
Thus, you need to perform these steps to use Entity Framework Core in your application. Visit Saving Data and Querying chapters to learn more about saving and retrieving data in EF Core.

Entity Framework Core: DbContext

The DbContext class is an integral part of Entity Framework. An instance of DbContextrepresents a session with the database which can be used to query and save instances of your entities to a database. DbContext is a combination of the Unit Of Work and Repository patterns.
DbContext in EF Core allows us to perform following tasks:
  1. Manage database connection
  2. Configure model & relationship
  3. Querying database
  4. Saving data to the database
  5. Configure change tracking
  6. Caching
  7. Transaction management
To use DbContext in our application, we need to create the class that derives from DbContext, also known as context class. This context class typically includes DbSet<TEntity> properties for each entity in the model. Consider the following example of context class in EF Core.
public class SchoolContext : DbContext
{
    public SchoolContext()
    {
  
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
    }
    //entities
    public DbSet<Student> Students { get; set; }
    public DbSet<Course> Courses { get; set; }
} 
In the example above, the SchoolContext class is derived from the DbContext class and contains the DbSet<TEntity> properties of Student and Course type. It also overrides the OnConfiguring and OnModelCreating methods. We must create an instance of SchoolContext to connect to the database and save or retrieve Student or Course data.
The OnConfiguring() method allows us to select and configure the data source to be used with a context using DbContextOptionsBuilder. Learn how to configure a DbContext class at here.
The OnModelCreating() method allows us to configure the model using ModelBuilderFluent API.

DbContext Methods

MethodUsage
AddAdds a new entity to DbContext with Added state and starts tracking it. This new entity data will be inserted into the database when SaveChanges() is called.
AddAsyncAsynchronous method for adding a new entity to DbContext with Added state and starts tracking it. This new entity data will be inserted into the database when SaveChangesAsync() is called.
AddRangeAdds a collection of new entities to DbContext with Added state and starts tracking it. This new entity data will be inserted into the database when SaveChanges() is called.
AddRangeAsyncAsynchronous method for adding a collection of new entities which will be saved on SaveChangesAsync().
AttachAttaches a new or existing entity to DbContext with Unchanged state and starts tracking it.
AttachRangeAttaches a collection of new or existing entities to DbContext with Unchanged state and starts tracking it.
EntryGets an EntityEntry for the given entity. The entry provides access to change tracking information and operations for the entity.
FindFinds an entity with the given primary key values.
FindAsyncAsynchronous method for finding an entity with the given primary key values.
RemoveSets Deleted state to the specified entity which will delete the data when SaveChanges() is called.
RemoveRangeSets Deleted state to a collection of entities which will delete the data in a single DB round trip when SaveChanges() is called.
SaveChangesExecute INSERT, UPDATE or DELETE command to the database for the entities with Added, Modified or Deleted state.
SaveChangesAsyncAsynchronous method of SaveChanges()
SetCreates a DbSet<TEntity> that can be used to query and save instances of TEntity.
UpdateAttaches disconnected entity with Modified state and start tracking it. The data will be saved when SaveChagnes() is called.
UpdateRangeAttaches a collection of disconnected entities with Modified state and start tracking it. The data will be saved when SaveChagnes() is called.
OnConfiguringOverride this method to configure the database (and other options) to be used for this context. This method is called for each instance of the context that is created.
OnModelCreatingOverride this method to further configure the model that was discovered by convention from the entity types exposed in DbSet<TEntity> properties on your derived context.

DbContext Properties

MethodUsage
ChangeTrackerProvides access to information and operations for entity instances this context is tracking.
DatabaseProvides access to database related information and operations for this context.
ModelReturns the metadata about the shape of entities, the relationships between them, and how they map to the database.
Learn how to create the first simple EF Core console application in the next chapter.

Creating a Model for an Existing Database in Entity Framework Core

Here you will learn how to create the context and entity classes for an existing database in Entity Framework Core. Creating entity & context classes for an existing database is called Database-First approach.
EF Core does not support visual designer for DB model and wizard to create the entity and context classes similar to EF 6. So, we need to do reverse engineering using the Scaffold-DbContext command. This reverse engineering command creates entity and context classes (by deriving DbContext) based on the schema of the existing database.
Let's create entity and context classes for the following SchoolDB database in the local MS SQL Server shown below.

Scaffold-DbContext Command

Use Scaffold-DbContext to create a model based on your existing database. The following parameters can be specified with Scaffold-DbContext in Package Manager Console:
Scaffold-DbContext [-Connection] [-Provider] [-OutputDir] [-Context] [-Schemas>] [-Tables>] 
                    [-DataAnnotations] [-Force] [-Project] [-StartupProject] [<CommonParameters>]
In Visual Studio, select menu Tools -> NuGet Package Manger -> Package Manger Console and run the following command:
PM> Scaffold-DbContext "Server=.\SQLExpress;Database=SchoolDB;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models
In the above command, the first parameter is a connection string which includes three parts: DB Server, database name and security info. Here, Server=.\SQLExpress; refers to local SQLEXPRESS database server. Database=SchoolDB; specifies the database name "SchoolDB" for which we are going to create classes. Trusted_Connection=True;specifies the Windows authentication. It will use Windows credentials to connect to the SQL Server. The second parameter is the provider name. We use provider for the SQL Server, so it is Microsoft.EntityFrameworkCore.SqlServer. The -OutputDir parameter specifies the directory where we want to generate all the classes which is the Models folder in this case.
Use the following command to get the detailed help on Scaffold-DbContext command:
PM> get-help scaffold-dbcontext –detailed
The above Scaffold-DbContext command creates entity classes for each table in the SchoolDB database and context class (by deriving DbContext) with Fluent API configurations for all the entities in the Models folder.
The following is the generated Student entity class for the Student table.
using System;
using System.Collections.Generic;

namespace EFCoreTutorials.Models
{
    public partial class Student
    {
        public Student()
        {
            StudentCourse = new HashSet<StudentCourse>();
        }

        public int StudentId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int? StandardId { get; set; }

        public Standard Standard { get; set; }
        public StudentAddress StudentAddress { get; set; }
        public ICollection<StudentCourse> StudentCourse { get; set; }
    }
}
The following is the SchoolDBContext class which you can use to save or retrieve data.
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;

namespace EFCoreTutorials.Models
{
    public partial class SchoolDBContext : DbContext
    {
        public virtual DbSet<Course> Course { get; set; }
        public virtual DbSet<Standard> Standard { get; set; }
        public virtual DbSet<Student> Student { get; set; }
        public virtual DbSet<StudentAddress> StudentAddress { get; set; }
        public virtual DbSet<StudentCourse> StudentCourse { get; set; }
        public virtual DbSet<Teacher> Teacher { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. See http://go.microsoft.com/fwlink/?LinkId=723263 for guidance on storing connection strings.
                optionsBuilder.UseSqlServer(@"Server=.\SQLExpress;Database=SchoolDB;Trusted_Connection=True;");
            }
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Course>(entity =>
            {
                entity.Property(e => e.CourseName)
                    .HasMaxLength(50)
                    .IsUnicode(false);

                entity.HasOne(d => d.Teacher)
                    .WithMany(p => p.Course)
                    .HasForeignKey(d => d.TeacherId)
                    .OnDelete(DeleteBehavior.Cascade)
                    .HasConstraintName("FK_Course_Teacher");
            });

            modelBuilder.Entity<Standard>(entity =>
            {
                entity.Property(e => e.Description)
                    .HasMaxLength(50)
                    .IsUnicode(false);

                entity.Property(e => e.StandardName)
                    .HasMaxLength(50)
                    .IsUnicode(false);
            });

            modelBuilder.Entity<Student>(entity =>
            {
                entity.Property(e => e.StudentId).HasColumnName("StudentID");

                entity.Property(e => e.FirstName)
                    .HasMaxLength(50)
                    .IsUnicode(false);

                entity.Property(e => e.LastName)
                    .HasMaxLength(50)
                    .IsUnicode(false);

                entity.HasOne(d => d.Standard)
                    .WithMany(p => p.Student)
                    .HasForeignKey(d => d.StandardId)
                    .OnDelete(DeleteBehavior.Cascade)
                    .HasConstraintName("FK_Student_Standard");
            });

            modelBuilder.Entity<StudentAddress>(entity =>
            {
                entity.HasKey(e => e.StudentId);

                entity.Property(e => e.StudentId)
                    .HasColumnName("StudentID")
                    .ValueGeneratedNever();

                entity.Property(e => e.Address1)
                    .IsRequired()
                    .HasMaxLength(50)
                    .IsUnicode(false);

                entity.Property(e => e.Address2)
                    .HasMaxLength(50)
                    .IsUnicode(false);

                entity.Property(e => e.City)
                    .IsRequired()
                    .HasMaxLength(50)
                    .IsUnicode(false);

                entity.Property(e => e.State)
                    .IsRequired()
                    .HasMaxLength(50)
                    .IsUnicode(false);

                entity.HasOne(d => d.Student)
                    .WithOne(p => p.StudentAddress)
                    .HasForeignKey<StudentAddress>(d => d.StudentId)
                    .HasConstraintName("FK_StudentAddress_Student");
            });

            modelBuilder.Entity<StudentCourse>(entity =>
            {
                entity.HasKey(e => new { e.StudentId, e.CourseId });

                entity.HasOne(d => d.Course)
                    .WithMany(p => p.StudentCourse)
                    .HasForeignKey(d => d.CourseId)
                    .OnDelete(DeleteBehavior.ClientSetNull)
                    .HasConstraintName("FK_StudentCourse_Course");

                entity.HasOne(d => d.Student)
                    .WithMany(p => p.StudentCourse)
                    .HasForeignKey(d => d.StudentId)
                    .HasConstraintName("FK_StudentCourse_Student");
            });

            modelBuilder.Entity<Teacher>(entity =>
            {
                entity.Property(e => e.StandardId).HasDefaultValueSql("((0))");

                entity.Property(e => e.TeacherName)
                    .HasMaxLength(50)
                    .IsUnicode(false);

                entity.HasOne(d => d.Standard)
                    .WithMany(p => p.Teacher)
                    .HasForeignKey(d => d.StandardId)
                    .OnDelete(DeleteBehavior.Cascade)
                    .HasConstraintName("FK_Teacher_Standard");
            });
        }
    }
}

Note: EF Core creates entity classes only for tables and not for StoredProcedures or Views.

DotNet CLI

If you use dotnet command line interface to execute EF Core commands then open command prompt and navigate to the root folder and execute the following dotnet ef dbcontext scaffold command:
> dotnet ef dbcontext scaffold "Server=.\SQLEXPRESS;Database=SchoolDB;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -o Models 
Thus, you can create EF Core model for an existing database.
Note: Once you have created the model, you must use the Migration commands whenever you change the model to keep the database up to date with the model.

Install Entity Framework Core

Entity Framework Core can be used with .NET Core or .NET 4.6 based applications. Here, you will learn to install and use Entity Framework Core 2.0 in .NET Core Console application using Visual Studio 2017.
EF Core is not a part of .NET Core or standard .NET framework. It is available as a NuGet package. You need to install NuGet packages for the following two things to use EF Core in your application:
  1. EF Core DB provider
  2. EF Core tools
Let's install the above NuGet packages in the .NET Core console application in Visual Studio 2017.

Install EF Core DB Provider

As mentioned in the previous chapter, EF Core allows us to access databases via the provider model. There are different EF Core DB providers available for the different databases. These providers are available as NuGet packages.
First, we need to install the NuGet package for the provider of the database we want to access. Here, we want to access MS SQL Server database, so we need to install Microsoft.EntityFrameworkCore.SqlServer NuGet package.
To install the DB provider NuGet package, right click on the project in the Solution Explorer in Visual Studio and select Manage NuGet Packages.. (or select on the menu: Tools -> NuGet Package Manager -> Manage NuGet Packages For Solution).
This will open NuGet Package Manager UI. Click on the Browse or the Updates tab and search for Microsoft.entityframeworkcore in the search box at the top left corner, as shown below.
Choose the provider package for the database you want to access. In this case select Microsoft.EntityFrameworkCore.SqlServer for MS SQL Server as shown above. (make sure that it has the .NET symbol and the Author is Microsoft). Click Install to start the installation.
The preview popup displays the list of packages it is going to install in your application. Review the changes and click OK.
Finally, accept the license terms associated with the packages that are going to be installed.
This will install the Microsoft.EntityFrameworkCore.SqlServer package. Verify it in Dependencies -> NuGet, as shown below.
Notice that the provider NuGet package also installed other dependent packages such as Microsoft.EntityFrameworkCore.Relational and System.Data.SqlClient.
Alternatively, you can also install provider's NuGet package using Package Manager Console. Go to Tools -> NuGet Package Manager -> Package Manager Console and execute the following command to install SQL Server provider package:
PM> Install-Package Microsoft.EntityFrameworkCore.SqlServer

Install EF Core Tools

Along with the DB provider package, you also need to install EF tools to execute EF Core commands. These make it easier to perform several EF Core-related tasks in your project at design time, such as migrations, scaffolding, etc.
EF Tools are available as NuGet packages. You can install NuGet package for EF tools depending on where you want to execute commands: either using Package Manager Console (PowerShell version of EF Core commands) or using dotnet CLI.

Install EF Core Tools for PMC

In order to execute EF Core commands from Package Manager Console, search for the Microsoft.EntityFrameworkCore.Tools package from NuGet UI and install it as shown below.
This will allow you to execute EF Core commands for scaffolding, migration etc. directly from Package Manager Console (PMC) within Visual Studio.

Install EF Core Tools for dotnet CLI

If you want to execute EF Core commands from .NET Core's CLI (Command Line Interface), first install the NuGet package Microsoft.EntityFrameworkCore.Tools.DotNet using NuGet UI.
After installing Microsoft.EntityFrameworkCore.Tools.DotNet package, edit the .csproj file by right clicking on the project in the Solution Explorer and select Edit <projectname>.csproj. Add <DotNetCliToolReference> node as shown below. This is an extra step you need to perform in order to execute EF Core 2.0 commands from dotnet CLI in VS2017.
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.0.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.0" />
    <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
  </ItemGroup>
</Project>
Now, open the command prompt (or terminal) from the root folder of your project and execute EF Core commands from CLI starting with dotnet ef, as shown below.

Thus, you can install required packages for EF Core 2.0 to get started.