Руководство по entity framework core

Данное руководство устарело. Актуальное руководство: Руководство по Entity Framework Core 7

Последнее обновление: 27.02.2021

  1. Глава 1. Введение в Entity Framework Core

    1. Что такое Entity Framework Core

    2. Первое приложение на EF Core

    3. Подключение к существующей базе данных

    4. Основные операции с данными. CRUD

    5. Конфигурация подключения

    6. Логгирование операций

    7. Провайдеры логгирования

  2. Глава 2. Создание моделей в Entity Framework Core

    1. Модели, Fluent API и аннотации данных

    2. Управление схемой БД и миграции

    3. Определение моделей

    4. Свойства моделей

    5. Сопоставление таблиц и столбцов

    6. Настройка ключей

    7. Настройка индексов

    8. Генерация значений свойств и столбцов

    9. Ограничения свойств

    10. Типы данных

    11. Конфигурация моделей

    12. Инициализация базы данных начальными данными

  3. Глава 3. Отношения между моделями

    1. Внешние ключи и навигационные свойства

    2. Каскадное удаление

    3. Загрузка связанных данных. Метод Include

    4. Explicit loading

    5. Lazy loading

    6. Отношение один к одному

    7. Отношение один ко многим

    8. Отношение многие ко многим

    9. Комплексные типы

  4. Глава 4. Наследование

    1. Подход TPH — Table Per Hierarchy

    2. Подход TPT — Table Per Type

  5. Глава 5. Запросы и LINQ to Entities

    1. Введение в LINQ to Entities

    2. Выборка и фильтрация

    3. Сортировка и проекция из базы данных

    4. Соединение и группировка таблиц

    5. Операции с множествами: объединение, пересечение, разность

    6. Агрегатные операции

    7. Отслеживание объектов и AsNoTracking

    8. Выполнение запросов

    9. IEnumerable и IQueryable

    10. Фильтры запросов уровня модели

  6. Глава 6. SQL в Entity Framework Core

    1. Выполнение SQL-запросов

    2. Хранимые функции

    3. Хранимые процедуры

  7. Глава 7. Провайдеры баз данных

    1. PostgreSQL

    2. MySQL

  8. Глава 8. Дополнительные статьи

    1. Параллелизм

    2. Скомпилированные запросы

    3. Проекция запросов на представления

  • Глава 1. Введение в Entity Framework Core
    • Что такое Entity Framework Core
    • Первое приложение на EF Core
    • Подключение к существующей базе данных
    • Основные операции с данными. CRUD
    • Конфигурация подключения
    • Логгирование операций
    • Провайдеры логгирования
  • Глава 2. Создание моделей в Entity Framework Core
    • Модели, Fluent API и аннотации данных
    • Управление схемой БД и миграции
    • Определение моделей
    • Свойства моделей
    • Сопоставление таблиц и столбцов
    • Настройка ключей
    • Настройка индексов
    • Генерация значений свойств и столбцов
    • Ограничения свойств
    • Типы данных
    • Конфигурация моделей
    • Инициализация базы данных начальными данными
  • Глава 3. Отношения между моделями
    • Внешние ключи и навигационные свойства
    • Каскадное удаление
    • Загрузка связанных данных. Метод Include
    • Explicit loading
    • Lazy loading
    • Отношение один к одному
    • Отношение один ко многим
    • Отношение многие ко многим
    • Комплексные типы
  • Глава 4. Наследование
    • Подход TPH — Table Per Hierarchy
    • Подход TPT — Table Per Type
  • Глава 5. Запросы и LINQ to Entities
    • Введение в LINQ to Entities
    • Выборка и фильтрация
    • Сортировка и проекция из базы данных
    • Соединение и группировка таблиц
    • Операции с множествами: объединение, пересечение, разность
    • Агрегатные операции
    • Отслеживание объектов и AsNoTracking
    • Выполнение запросов
    • IEnumerable и IQueryable
    • Фильтры запросов уровня модели
  • Глава 6. SQL в Entity Framework Core
    • Выполнение SQL-запросов
    • Хранимые функции
    • Хранимые процедуры
  • Глава 7. Провайдеры баз данных
    • PostgreSQL
    • MySQL
  • Глава 8. Дополнительные статьи
    • Параллелизм
    • Компилируемые запросы
    • Проекция запросов на представления

YooMoney:

410011174743222

Перевод на карту

Номер карты:

4048415020898850

Время на прочтение
2 мин

Количество просмотров 6.6K

Всем привет!

В этой части будет кратко рассказано о технологии Entity Framework Core, а также её установки в свой проект.

Кратко о технологии

Общая цель EF — предоставить возможность взаимодействия с данными из реляционных баз данных с использованием объектной модели, которая отображается напрямую на бизнес-объекты в создаваемых приложениях. Например, вместо того, чтобы трактовать пакет данных как коллекцию строк и столбцов, вы можете оперировать с коллекцией строго типизированных объектов, называемых сущностями. Такие сущности хранятся в специализированных классах коллекций, поддерживающих LINQ, что позволяет выполнять операции доступа к данным в коде С#.

Entity Framework Core (также известная как EF Core) пред­ставляет собой пакет объектно-реляционного отображения (object-relational mapping — ОRМ) производства Microsoft, который позволяет приложениям .NET Core хранить данные в реляционных базах данных. Entity Framework Core решает одну основную задачу: сохранение объектов .NET в базе данных (БД) и извлечение их в более позднее время.

Более подробно можете почитать на здесь и здесь.

Создание проекта

Для начала создадим консольное приложение в Visual Studio 2022. Как вариант, это можно сделать сразу через консоль:

dotnet new sln -n EntityFrameworkBase #создаем проект 
dotnet new console -lang c# -n ChapterZero -o .ChapterZero -f net6.0  #создаем решение
dotnet sln .EntityFrameworkBase.sln add .ChapterZero  #прикрепляем к проекту решение

Или запускайте Visual Studio и выполняйте пошагово действия, которые показаны ниже:

  1. Создаем новый проект.

Создать новый проект.

Создать новый проект.
  1. Создаем консольное приложение.

Выбрать консольное приложение.

Выбрать консольное приложение.
  1. Придумываем название проекта, выбираем путь, по которому будет располагаться проект, и, наконец, придумываем название решения.

Создать решение.

Создать решение.

После выполненных действий в обозревателе решений (Solution Explorer) должно быть отображено следующее:

Созданный проект

Созданный проект

Установка EF Core в свой проект

Установить все нужные библиотеки можно как и выше через консоль:

dotnet add ChapterZero package Microsoft.EntityFrameworkCore.Design
dotnet add ChapterZero package Microsoft.EntityFrameworkCore.SqlServer
dotnet add ChapterZero package Microsoft.EntityFrameworkCore

Или в Visual Studio через NuGet Package Manager. Процесс показан ниже:

  1. Переходим в Tools / NuGet Package Manager / Manage NuGet Packages for Solution.

Переходим в Manage NuGet Packages

Переходим в Manage NuGet Packages
  1. Переходим в Browse и ищем Microsoft.EntityFrameworkCore.

  1. Устанавливаем в проект.

Для Microsoft.EntityFrameworkCore.SqlServer и Microsoft.EntityFrameworkCore.Design повторить тоже самое. После установки всех нужных компонентов при переходе во вкладку Installed должно отобразиться следующее:

На этом пока что закончим. Код проекта тут.

Спасибо вам за прочтение.

Table of Contents

  • Introduction
  • Background
  • What is an Object Relational Mapper?
  • What is Entity Framework Core?
  • Design WorkFlows
  • Let’s Begin!
    • Database Creation
    • Creating an ASP.NET Core Project
    • First Run
    • The Model
  • Integrating Entity Framework Core
    • Using Package Manager Console
    • Using Nuget Package Manager GUI
  • Creating Entity Models from Existing Database
    • Option 1: Using Package Manager Console
    • Option 2: Using Command Window
    • Student Class
    • EFCoreDBFirstDemoContext Class
  • Registering DBContext using Dependency Injection
  • Scaffolding a Controller with Views
  • The Generated Code
  • Testing the Application
  • Summary
  • Download Source Code
  • See Also

Introduction

This article is the first part of the series on Getting Started with Entity Framework Core. In this post, we will build an ASP.NET Core MVC application that performs basic data access using Entity Framework Core. We will explore
the database-first approach and see how models are created from an existing database. We will also take a look at how classes become the link between the database and ASP.NET Core MVC Controller.

Background

In Software Development world, most applications require data store or database. So we all need a code to read/write our data stored in database or data store. Creating and maintaining code for database is a tedious work and
it is a real challenge for us. That’s where Object Relational Mappers like Entity Framework comes into place.

What is an Object Relational Mapper?

An ORM enables developers to create data access applications by programming against a conceptual application model instead of programming directly against a relational storage schema. The goal is to decrease the amount of code
and maintenance required for data-oriented applications. ORM like Entity Framework Core provides the following benefits:

  • Applications can work in terms of a more application-centric conceptual model, including types with inheritance, complex members, and relationships.
  • Applications are freed from hard-coded dependencies on a particular data engine or storage schema.
  • Mappings between the conceptual model and the storage-specific schema can change without changing the application code.
  • Developers can work with a consistent application object model that can be mapped to various storage schemas, possibly implemented in different database management systems.
  • Multiple conceptual models can be mapped to a single storage schema.
  • Language-integrated query (LINQ) support provides compile-time syntax validation for queries against a conceptual model.

What is Entity Framework Core?

According to the official
documentation: Entity Framework (EF) Core is a lightweight, extensible, and cross-platform version of the popular Entity Framework data access technology. EF Core is an object-relational mapper (O/RM) that enables .NET developers to work with a database
using .NET objects. It eliminates the need for most of the data-access code that developers usually need to write.

Design WorkFlows

Just like any other ORM, there are two main design workflows that is supported by Entity Framework Core: The Code-First approach which is you create your classes (POCO Entities) and generate a new database out from it. The Database-First
approach allows you to use an existing database and generate classes based on your database schema. In the part let’s explore Database-First approach first.

If you are still confuse about the difference and advantages between the two, don’t worry as we will be covering that in much detail in the upcoming part of the series.

Let’s Begin!

If you’re ready to explore EF Core database-first development and get your hands dirty, then let’s get started!

Database Creation

In this walk-through, we will just be creating a single database table that houses some simple table properties for simplicity. Go ahead and open Microsoft SQL Server Management Studio and run the following SQL script below to
create the database and table:

CREATE
DATABASE
EFCoreDBFirstDemo 

GO

USE [EFCoreDBFirstDemo] 

GO

CREATE
TABLE
[dbo].[Student]( 

 [StudentId] [bigint] IDENTITY(1,1)
NOT NULL,

 [FirstName] [varchar](30)
NULL,

 [LastName] [varchar](30)
NULL,

 [Gender] [varchar](10)
NULL,

 [DateOfBirth] [datetime]
NULL,

 [DateOfRegistration] [datetime]
NULL,

 [PhoneNumber] [varchar](20)
NULL,

 [Email] [varchar](50)
NULL,

 [Address1] [varchar](50)
NULL,

 [Address2] [varchar](50)
NULL,

 [City] [varchar](30)
NULL,

 [State] [varchar](30)
NULL,

 [Zip] [nchar](10)
NULL,

CONSTRAINT
[PK_Student] PRIMARY
KEY
CLUSTERED 

(

 [StudentId]
ASC

)WITH
(PAD_INDEX = OFF, STATISTICS_NORECOMPUTE =
OFF,

 IGNORE_DUP_KEY =
OFF, ALLOW_ROW_LOCKS =
ON,

 ALLOW_PAGE_LOCKS =
ON)
ON
[
PRIMARY]

)
ON
[
PRIMARY]

GO

The SQL script above should create the “EFCoreDBFirstDemo” database with the following table: 


Figure 1: dbo.Student SQL Table

Nothing really fancy there. The dbo.Student table just contains some basic properties for us to use in our web application later on.

Creating an ASP.NET Core Project

Our next step is to create a web page where we can send and retrieve data from our database.

Fire-up Visual Studio 2017 and let’s create a new ASP.NET Core Web Application project. To do this, select File > New > Project. In the New Project dialog select Visual C# > Web > ASP.NET Core Web Application (.NET Core) just
like in the figure below:


Figure 2: ASP.NET Core Web Application Project Template

Name your project to whatever you like, but for this exercise, we will name it as “DBFirstDevelopment” to conform with the topic. Click OK and then select “Web Application” within ASP.NET Core
templates as shown in the following figure below:


Figure 3: ASP.NET Core Web Application Template

Now click OK to let Visual Studio generate the required files needed for us to run the application. The figure below shows the default generated files:


Figure 4: Default ASP.NET Core Web Application Project Files

If you are not familiar with the new file structure generated in ASP.NET Core, then don’t worry as we will get to know them in the next part of this series. For now, let’s just keep moving.

First Run

Let’s build and run the application by pressing CTRL + F5 or clicking on the IIS Express Play button at the toolbar. If you are seeing the following output below, then congrats, you now have a running ASP.NET Core web application.


Figure 5: First Run

The Model

Let’s create a “Models” folder within the root of the application and under that folder, create another folder and name it as “DB”. Our project structure should now look something like below:


Figure 6: The Models Folder

The “DB» folder will contain our DBContextand Entity models. We are going to use Entity Framework Core as our data access mechanism to work with database. We will not be using the old-fashioned Entity Framework designer to generate
models for us because EF designer (EDMX) isn’t supported in ASP.NET Core 1.1.

Integrating Entity Framework Core

ASP.NET Core is designed to be light-weight, modular and pluggable. This allows us to plug-in components that are only required for our project. Having said that, we need to add the Entity Framework Core packages in our ASP.NET
Core app because we are going to need it. For more details about Entity Framework Core then check out the references section at the end of this article.

There are two ways to add packages in ASP.NET Core; you could either use the Package Manager Console, or via NuGet Package Manager (NPM). In this exercise, we are going to use NPM so you can have a visual reference.

Now, right-click on the root of your application and then select Manage NuGet Packages. Select the Browse tab and in the search bar, type in “Microsoft.EntityFrameworkCore.SqlServer”. It should result to something like this:


Figure 7: Manage NuGet Package

Select “Microsoft.EntityFrameworkCore.SqlServer” and click Install. The latest stable version as of this time of writing is v1.1.2. You would probably be prompted with the Review Changes and License Acceptance dialog. Just click
“I Accept” and follow the wizard instructions until it completes the installation.

Since we are going to use Database-First development approach to work with existing database, we need to install the additional packages below as well:

  • Microsoft.EntityFrameworkCore.Tools (v1.1.1)
  • Microsoft.EntityFrameworkCore.SqlServer.Design (v1.1.2)

Now, go ahead and install them via Package Manager Console or Nuget Package Manager (NPM) GUI.

Using Package Manager Console

Go to Tools > NuGet Package Manager -> Package Manager Console and run the following command individually:

Install-Package Microsoft.EntityFrameworkCore.Tools 

Install-Package Microsoft.EntityFrameworkCore.SqlServer.Design

Using Nuget Package Manager GUI

Go to Tools > NuGet Package Manager > Manage NuGet Packages for Solution, then type in «Microsoft.EntityFrameworkCore.Tools». Click install and then do the same with «Microsoft.EntityFrameworkCore.SqlServer.Design».

Notes:

  1. It is always recommended to install the latest stable version of each package to avoid unexpected errors during development. 
  2. If you are getting this error “Unable to resolve ‘Microsoft.EntityFrameworkCore.Tools (>= 1.1.2)’ for ‘.NETCoreApp,Version=v1.1’” when building the project, then just restart Visual Studio 2017.

When it’s done restoring all the required packages, you should be able to see them added to your project dependencies as shown in the figure below:


Figure 8: Entity Framework Core Packages Restored

For details about the new features in Entity Framework Core, check out the references section at the end of this article.

Creating Entity Models from Existing Database

Now, it’s time for us to create the Entity Framework models based on our existing database that we have just created earlier.

As of this time of writing, there are two ways to generate models from our existing database (a.k.a reverse engineer).

Option 1: Using Package Manager Console

  • Go to Tools –> NuGet Package Manager –> Package Manager Console
  • And then run the following command below to create a model from the existing database:

Scaffold-DbContext
"Server=SSAI-L0028-HPSQLEXPRESS;Database=EFCoreDBFirstDemo;Trusted_Connection=True;"
Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models/DB

The Server attribute above is the SQL server instance name. You can find it in SQL Management Studio by right clicking on your database. For this example, the server name is “SSAI-L0028-HPSQLEXPRESS”.

The Database attribute is your database name. In this case, the database name is “EFCoreDBFirstDemo”.

The –OutputDir attribute allows you to specify the location of the files generated. In this case, we’ve set it to Models/DB.

Option 2: Using Command Window

If for some unknown reason Option 1 will not work for you, try the following instead:

  • Go to the root folder of your application. In this case the “DBFirstDevelopment”.
  • Do a Shift + Right Click and select “Open command window here” Then run the following script:

dotnet ef dbcontext scaffold
"Server=SSAI-L0028-HPSQLEXPRESS;Database=EFCoreDBFirstDemo;Trusted_Connection=True;"
Microsoft.EntityFrameworkCore.SqlServer --output-dir Models/DB

The script above is quite similar to the script that we used in Option 1, except we use the command dotnet ef dbcontext scaffold and —output-dir to set the target location of the scaffold files.

Note that you need to change the Server value based on your database server configuration. If you are using a different database name, you would need to change the Database value too.

The command above will generate models from existing database within Models/DB folder. Here’s the screenshot below:


Figure 9: Entity Framework Generated Models

Tips

If you are still getting errors then you might want to upgrade the PowerShell to version 5.

  • You can download it here:
    https://www.microsoft.com/en-us/download/details.aspx?id=50395
  • You need to change the value of the Server and Database attributes in your connection string based on your server configuration.

The reverse engineer process created the Student.cs entity class and a derived context (EFCoreDBFirstDemoContext.cs) based on the schema of the existing database.

The following are the codes generated.

Student Class

using
System; 

using
System.Collections.Generic;

namespace
DBFirstDevelopment.Models.DB 

{

       public
partial
class
Student

       {

           public
long
StudentId {
get;
set; }

           public
string
FirstName {
get;
set; }

           public
string
LastName {
get;
set; }

           public
string
Gender {
get;
set; }

           public
DateTime? DateOfBirth { get;
set; }

           public
DateTime? DateOfRegistration { get;
set; }

           public
string
PhoneNumber {
get;
set; }

           public
string
Email {
get;
set; }

           public
string
Address1 {
get;
set; }

           public
string
Address2 {
get;
set; }

           public
string
City {
get;
set; }

           public
string
State {
get;
set; }

           public
string
Zip {
get;
set; }

      }

}

The entity class above is nothing but a simple C# object that represents the data you will be querying and saving.

EFCoreDBFirstDemoContext Class

using
System; 

using
Microsoft.EntityFrameworkCore; 

using
Microsoft.EntityFrameworkCore.Metadata;

namespace
DBFirstDevelopment.Models.DB 

{

    public
partial
class
EFCoreDBFirstDemoContext : DbContext

    {

        public
virtual
DbSet<Student> Student {
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=SSAI-L0028-HPSQLEXPRESS;Database=EFCoreDBFirstDemo;Trusted_Connection=True;");

            }

        }

        protected
override
void
OnModelCreating(ModelBuilder modelBuilder)

        {

            modelBuilder.Entity<Student>(entity =>

            {

                entity.Property(e => e.Address1).HasColumnType("varchar(50)");

                entity.Property(e => e.Address2).HasColumnType("varchar(50)");

                entity.Property(e => e.City).HasColumnType("varchar(30)");

                entity.Property(e => e.DateOfBirth).HasColumnType("datetime");

                entity.Property(e => e.DateOfRegistration).HasColumnType("datetime");

                entity.Property(e => e.Email).HasColumnType("varchar(50)");

                entity.Property(e => e.FirstName).HasColumnType("varchar(30)");

                entity.Property(e => e.Gender).HasColumnType("varchar(10)");

                entity.Property(e => e.LastName).HasColumnType("varchar(30)");

                entity.Property(e => e.PhoneNumber).HasColumnType("varchar(20)");

                entity.Property(e => e.State).HasColumnType("varchar(30)");

                entity.Property(e => e.Zip).HasColumnType("nchar(10)");

            });

        }

    }

}

The EFCoreDBFirstDemoContext class represents a session with the database and allows you to query and save instances of the entity classes.

If you have noticed, the models generated are created as partial classes. This means that you can extend them by creating another partial class for each of the entity/model classes when needed.

Registering DBContext using Dependency Injection

The next step is to register our EFCoreDBFirstDemoContext class using Dependency Injection (DI). To follow the ASP.NET Core configuration pattern, we will move the database provider configuration to Startup.cs. To do this, just follow these steps:

(1) Open ModelsDBEFCoreDBFirstDemoContext.cs file

(2) Remove the OnConfiguring() method and add the following code below:

public
EFCoreDBFirstDemoContext(DbContextOptions<EFCoreDBFirstDemoContext> options) 

:
base(options)

{ }

The constructor above will allow configuration to be passed into the context by dependency injection.

(3) Open appsettings.json file and add the following script for our database connection string below:

,

"ConnectionStrings": {

i.
"EFCoreDBFirstDemoDatabase":
"Server=SSAI-L0028-HP\SQLEXPRESS;Database=EFCoreDBFirstDemo;Trusted_Connection=True;" 

}

(4) Open Startup.cs

(5) Add the following using statements at the start of the file:

using
DBFirstDevelopment.Models; 

using
DBFirstDevelopment.Models.DB; 

using
Microsoft.EntityFrameworkCore;

(6) Add the following line of code within ConfigureServices() method:

// Add ASPNETCoreDemoDBContext services.

services.AddDbContext<EFCoreDBFirstDemoContext>(options => options.UseSqlServer(Configuration.GetConnectionString("EFCoreDBFirstDemoDatabase")));

That’s it. Now we’re ready to work with data.

Scaffolding a Controller with Views

Now that our data access is in place, we are now ready to work with data by creating an ASP.NET Core MVC Controller for handling data and performing basic Create, Read, Update and Delete (CRUD) operations.

Enable scaffolding in the project:

  1. Right-click on the Controllers folder in Solution Explorer and select Add > Controller.
  2. Select Full Dependencies and click Add.
  3. You can ignore or delete the ScaffoldingReadMe.txt file.

Now that scaffolding is enabled, we can scaffold a controller for the Student entity or just generate an Empty Controller. In this example, we are going to generate a Controller with Views using Entity Framework.

  1. Right-click on the Controllers folder in Solution Explorer and select Add > Controller.
  2. Select MVC Controller with Views using Entity Framework.
  3. Click Add.

In the “Add Controller” dialog, do the following:

  1. Select Student as Model class.
  2. Select EFCoreDBFirstDemoContext as the Data Context class
  3. Tick the Generate Views option
  4. In the “Use a layout page” option, browse through Views > Shared > _Layout.cshtml

Here’s a screen capture of the example: 


Figure 10: Add Controller Dialog

Just Click Add. The scaffolding will generate a Controller and a bunch of View files needed to run the application. Here’s a screen capture of the scaffold files:


Figure 11: The generated files

As you can see, scaffolding saves you a lot of time and effort as you don’t have to worry about generating your Views. All methods for performing CRUD operations are also generated for you without coding on your part, thus boosting your productivity. If there
is a need for you to change something in the View or in the Controller methods, it would probably be minimal.

The Generated Code

Here’s the generated code for the StudentsController class.

using
DBFirstDevelopment.Models.DB; 

using
Microsoft.AspNetCore.Mvc; 

using
Microsoft.EntityFrameworkCore; 

using
System.Linq; 

using
System.Threading.Tasks;

namespace
DBFirstDevelopment.Controllers 

{

    public
class
StudentsController : Controller

    {

        private
readonly
EFCoreDBFirstDemoContext _context;

        public
StudentsController(EFCoreDBFirstDemoContext context)

        {

            _context = context;   

        }

        // GET: Students

        public
async Task<IActionResult> Index()

        {

            return
View(await _context.Student.ToListAsync());

        }

        // GET: Students/Details/5

        public
async Task<IActionResult> Details(long? id)

        {

            if
(id == null)

            {

                return
NotFound();

            }

            var student = await _context.Student

                .SingleOrDefaultAsync(m => m.StudentId == id);

            if
(student == null)

            {

                return
NotFound();

            }

            return
View(student);

        }

        // GET: Students/Create

        public
IActionResult Create()

        {

            return
View();

        }

        // POST: Students/Create

        // To protect from overposting attacks, please enable the specific properties you want to bind to, for

        [HttpPost]

        [ValidateAntiForgeryToken]

        public
async Task<IActionResult> Create([Bind("StudentId,FirstName,LastName,Gender,DateOfBirth,DateOfRegistration,PhoneNumber,Email,Address1,Address2,City,State,Zip")] Student
student)

        {

            if
(ModelState.IsValid)

            {

                _context.Add(student);

                await _context.SaveChangesAsync();

                return
RedirectToAction("Index");

            }

            return
View(student);

        }

        // GET: Students/Edit/5

        public
async Task<IActionResult> Edit(long? id)

        {

            if
(id == null)

            {

                return
NotFound();

            }

            var student = await _context.Student.SingleOrDefaultAsync(m => m.StudentId == id);

            if
(student == null)

            {

                return
NotFound();

            }

            return
View(student);

        }

        // POST: Students/Edit/5

        // To protect from overposting attacks, please enable the specific properties you want to bind to, for

        [HttpPost]

        [ValidateAntiForgeryToken]

        public
async Task<IActionResult> Edit(long
id, [Bind("StudentId,FirstName,LastName,Gender,DateOfBirth,DateOfRegistration,PhoneNumber,Email,Address1,Address2,City,State,Zip")] Student student)

        {

            if
(id != student.StudentId)

            {

                return
NotFound();

            }

            if
(ModelState.IsValid)

            {

                try

                {

                    _context.Update(student);

                    await _context.SaveChangesAsync();

                }

                catch
(DbUpdateConcurrencyException)

                {

                    if
(!StudentExists(student.StudentId))

                    {

                        return
NotFound();

                    }

                    else

                    {

                        throw;

                    }

                }

                return
RedirectToAction("Index");

            }

            return
View(student);

        }

        // GET: Students/Delete/5

        public
async Task<IActionResult> Delete(long? id)

        {

            if
(id == null)

            {

                return
NotFound();

            }

            var student = await _context.Student

                .SingleOrDefaultAsync(m => m.StudentId == id);

            if
(student == null)

            {

                return
NotFound();

            }

            return
View(student);

        }

        // POST: Students/Delete/5

        [HttpPost, ActionName("Delete")]

        [ValidateAntiForgeryToken]

        public
async Task<IActionResult> DeleteConfirmed(long
id)

        {

            var student = await _context.Student.SingleOrDefaultAsync(m => m.StudentId == id);

            _context.Student.Remove(student);

            await _context.SaveChangesAsync();

            return
RedirectToAction("Index");

        }

        private
bool
StudentExists(
long
id)

        {

            return
_context.Student.Any(e => e.StudentId == id);

        }

    }

}

Let’s take a quick look of what we did above.

The StudentsController class uses Constructor Injection to gain access to the DBContext and DBSet defined within EFCoreDBFirstDemoContext. The DBContext contains virtual methods used to perform certain operations such as Add(), Update(), Find(), SaveChanges()
and many more. It also contains their corresponding asynchronous methods such as AddAsync(), FindAsync() , SaveChangesAsyn() and so on. The EFCoreDBFirstDemoContext class only contains a single DBSet based on our example, and that is the Student entity which
is defined as DBSet<Student>.

The Index() action method gets all Student records from the database. This method is defined as asynchronous which returns the corresponding Index View along with the list of Students entity.

The Details() action method gets the corresponding database record based on the parameter id. It returns NotFound() when the value of the parameter id is null, otherwise it fetches the record from the database based on the corresponding id using the LINQ syntax.
If the LINQ SingleOrDefaultAsync() method returns a row then the Details() method will return the Student model to the View, otherwise it returns NotFound().

The Create() action method simply returns it’s corresponding View. The overload Create() action method is decorated with the [HttpPost] attribute which signifies that the method can only be invoked for POST requests. This method is where we actually handle
the adding of new data to database if the model state is valid on posts.

The Edit() action method takes an id as the parameter. If the id is null, it returns NotFound(), otherwise it returns the corresponding data to the View. The overload Edit() action method updates the corresponding record to the database based on the id.

Just like the other action, the Delete() action method takes an id as the parameter. If the id is null, it returns NotFound(), otherwise it returns the corresponding data to the View. The overload Delete() action method deletes the corresponding record to the
database based on the id.

Testing the Application

Now build and run the application using CTRL + 5. In the browser, navigate to /students/Create. It should bring up the following page.


Figure 12: The Create New Student Page

Supply the fields in the page and then click the Create button. It should take you to the Index page with the inserted data as shown in the figure below:


Figure 13: The Index Page

Of course the Edit, viewing of Details and Delete works too without doing any code on your part. :)

Summary

In this part of the series, we’ve learned building an ASP.NET Core MVC application using Entity Framework Core with existing database. We also learned the power of scaffolding to quickly generate Controllers with Views based from Entity Framework model in just
a few clicks.

Download Source Code

See Also

  • Announcing New ASP.NET Core and Web Development Features in Visual Studio 2017
  • Getting Started with EF Core on ASP.NET Core with an Existing Database
  • Database Providers
  • Announcing the Fastest ASP.NET Yet, ASP.NET Core 1.1 RTM
  • Configuring a DbContext

Dotnet logo

.NET Tools
How-To’s

Getting Started With Entity Framework Core 5

With the .NET 5 release comes the newest Entity Framework Core version, unsurprisingly called Entity Framework Core 5 (EF Core 5). The .NET community got its first glimpse of Entity Framework (EF) on August 11th, 2008, with the release of .NET Framework 3.5 Service Pack 1. Let’s say a few things have changed since the initial release. For those unfamiliar with the technology, Entity Framework is an object-relational mapper (ORM), helping developers overcome the impedance mismatches between C# and a database instance’s schema.

Entity Framework has come a long way since its original inception. Conceived initially as a designer-centric tool heavily reliant on Visual Studio’s graphical user interface and SQL Server, EF’s current philosophy recognizes the need to be friendlier to ideas like distributed source-control, build servers, multiple database engines, and operating systems.

In this post, we’ll see how we can get started with EF Core 5. We’ll design a schema for entertainment productions using a code-first approach, migrate an SQLite database to our schema’s current incarnation, seed our database, and run some typical LINQ queries.

Getting started

To get started, I recommend folks install the latest version of the .NET 5 SDK. Technically, EF Core 5 can run on .NET Core 3.1, but aligning versions is always a good idea.

Starting with a brand new console application, we will need to install the following packages, making sure that all the versions are 5.0.0:

  • Microsoft.EntityFrameworkCore
  • Microsoft.EntityFrameworkCore.Design
  • Microsoft.EntityFrameworkCore.Sqlite

We can use the NuGet tool window to find and install all these packages into our project.

Additionally, we will want to install the dotnet-ef command-line tool (CLI). From within the newly created project directory type the following commands into Rider’s integrated terminal.

> dotnet new tool-manifest
> dotnet tool install dotnet-ef

When we finish setting up our project, we can run the dotnet-ef command, where the EF unicorn will greet us.

> dotnet ef

Great! We’re now ready to start modeling our problem domain.

Folks who want to follow along can clone a sample project from GitHub.

Code-first modeling

Every EF Core solution will have at least one DbContext implementation. What is DbContext? The DbContext is a specialized base class that tracks our in-memory operations, allowing us to write and execute queries, track changes that we can persist back to our database, and interact with our database engine indirectly or directly in exceptional circumstances.

To get started using EF Core 5, we need to define a class that derives from DbContext and contains our models. Typically, our classes will map one to one with our database tables, and relationships between tables are defined using navigational conventions. We’ll see how this works as we get into defining our classes.

Before diving into C# code, let’s discuss our topic. We’ll keep the subject simple, but we’ll also explore some important EF Core concepts. Our subject matter is Entertainment, both movies and television, which we’ll call Productions. A Production has characters and ratings. An Actor will always portray a Character. I hope as folks read these sentences, they start to formulate the relationships in their mind. Let’s look at our models, starting with a Production.

Table-per-hierarchy and inheritance

We had mentioned that both a Movie and a Series could be a Production. EF Core 5 applies an approach known as Table-per-hierarchy (TPH), where we store hierarchically related entities in a single table.

Each record’s type is distinguished by a Discriminator column, which usually holds a simplified C# type name. The Discriminator column is invisible to developers from C#, but we can see it in the database schema generated by EF Core 5.

public abstract class Production
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime Release { get; set; }
}

public class Movie : Production
{
    public int DurationInMinutes { get; set; }
    public double WorldwideBoxOfficeGross { get; set; }
}

public class Series : Production
{
    public int NumberOfEpisodes { get; set; }
}

The discriminator is an important mechanism that we’ll see used later in this post’s query section.

Relationships

When thinking about relational databases such as SQLite, SQL Server, PostgreSQL, and Oracle, we need to consider three kinds of relationships between our tables:

  1. Many to many
  2. One to many
  3. One to one

We have a many to many relationship between our Production and the Actors. We express this relationship through the Character entity. We also have a one to many relationship, where a Production can have many instances of a Rating.

public abstract class Production
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime Release { get; set; }

    public List<Character> Characters { get; set; } = new List<Character>();
    public List<Rating> Ratings { get; set; } = new List<Rating>();
}

public class Movie : Production
{
    public int DurationInMinutes { get; set; }
    public double WorldwideBoxOfficeGross { get; set; }
}

public class Series : Production
{
    public int NumberOfEpisodes { get; set; }
}

public class Rating
{
    public int Id { get; set; }
    public int ProductionId { get; set; }
    public Production Production { get; set; }
    public string Source { get; set; }
    public int Stars { get; set; }
}

public class Character
{
    public int Id { get; set; }
    public int ProductionId { get; set; }
    public Production Production { get; set; }
    public string Name { get; set; }
    public Actor Actor { get; set; }
    public int ActorId { get; set; }
}

public class Actor
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Character> Characters { get; set; } = new List<Character>();
}

Looking over our models, we can see some conventional patterns emerge. Naming is an essential part of EF Core modeling, where we define relationships by the Type of property definitions and their names. Let’s look at the Character definition.

We can see that the Character class has a one to one relationship to both an Actor entity and a Production entity. EF Core uses the naming convention of [Entity]Id to map values to the in-memory object instance. Developers can override EF Core conventions, but I recommend folks starting with EF Core use the default conventions.

Great, now that we have built our entities and relationships, let’s add them to our DbContext instance, which we’ll call EntertainmentDbContext. We’ll also want to define our database connection string for demo purposes.

 public class EntertainmentDbContext : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder options)
            => options.UseSqlite("Data Source=entertainment.db");

        public DbSet<Production> Productions { get; set; }
        public DbSet<Movie> Movies { get; set; }
        public DbSet<Series> Series { get; set; }
        public DbSet<Rating> Ratings { get; set; }
        public DbSet<Character> Characters { get; set; }
        public DbSet<Actor> Actors { get; set; }
    }

    public abstract class Production
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public DateTime Release { get; set; }

        public List<Character> Characters { get; set; } = new List<Character>();
        public List<Rating> Ratings { get; set; } = new List<Rating>();
    }

    public class Movie : Production
    {
        public int DurationInMinutes { get; set; }
        public double WorldwideBoxOfficeGross { get; set; }
    }

    public class Series : Production
    {
        public int NumberOfEpisodes { get; set; }
    }

    public class Rating
    {
        public int Id { get; set; }
        public int ProductionId { get; set; }
        public Production Production { get; set; }
        public string Source { get; set; }
        public int Stars { get; set; }
    }

    public class Character
    {
        public int Id { get; set; }
        public int ProductionId { get; set; }
        public Production Production { get; set; }
        public string Name { get; set; }
        public Actor Actor { get; set; }
        public int ActorId { get; set; }
    }

    public class Actor
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<Character> Characters { get; set; } = new List<Character>();
    }
}

Seeding initial data

There are multiple ways to insert data into our database, but it’s a good idea to use the data seed mechanism of EF Core for demos and necessary look-up values. In EF Core, we can override the OnModelCreating method on our EntertainmentDbContext.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    #region seed data

    var movies = new Movie[] {
        new Movie { Id = 1, Name = "Avengers: Endgame", WorldwideBoxOfficeGross = 2_797_800_564, DurationInMinutes = 181, Release = new DateTime(2019, 4, 26) },
        new Movie { Id = 2, Name = "The Lion King", WorldwideBoxOfficeGross = 1_654_791_102, DurationInMinutes     = 118, Release = new DateTime(2019, 7, 19) },
        new Movie { Id = 3, Name = "Ip Man 4", WorldwideBoxOfficeGross = 192_617_891, DurationInMinutes = 105, Release = new DateTime(2019, 12, 25) },
        new Movie { Id = 4, Name = "Gemini Man", WorldwideBoxOfficeGross = 166_623_705, DurationInMinutes = 116, Release = new DateTime(2019, 11, 20) },
        new Movie { Id = 5, Name = "Downton Abbey", WorldwideBoxOfficeGross = 194_051_302, DurationInMinutes = 120, Release = new DateTime(2020, 9, 20 )}
    };

    var series = new Series [] {
        new Series { Id = 6 , Name = "The Fresh Prince of Bel-Air", NumberOfEpisodes = 148, Release = new DateTime(1990, 9, 10) },
        new Series { Id = 7 , Name = "Downton Abbey", NumberOfEpisodes = 52, Release = new DateTime(2010, 09, 26) },
        new Series { Id = 8 , Name = "Stranger Things", NumberOfEpisodes = 34 , Release = new DateTime(2016, 7, 11) },
        new Series { Id = 9 , Name = "Kantaro: The Sweet Tooth Salaryman", NumberOfEpisodes = 12, Release = new DateTime(2017,7, 14) },
        new Series { Id = 10, Name = "The Walking Dead", NumberOfEpisodes = 177 , Release = new DateTime(2010, 10, 31) }
    };

    var productions = movies
        .Cast<Production>()
        .Union(series)
        .ToList();

    modelBuilder.Entity<Movie>().HasData(movies);
    modelBuilder.Entity<Series>().HasData(series);

    // characters
    modelBuilder.Entity<Character>().HasData(new Character []
    {
        // movies
        new Character { Id = 1, Name = "Tony Stark", ProductionId = 1, ActorId = 1 },
        new Character { Id = 2, Name = "Steve Rogers", ProductionId = 1, ActorId = 2 },
        new Character { Id = 3, Name = "Okoye", ProductionId = 1, ActorId = 3 },
        new Character { Id = 4, Name = "Simba", ProductionId = 2, ActorId = 4 },
        new Character { Id = 5, Name = "Nala", ProductionId = 2, ActorId = 5 },
        new Character { Id = 6, Name = "Ip Man", ProductionId = 3, ActorId = 6 },
        new Character { Id = 7, Name = "Henry Brogan", ProductionId = 4, ActorId = 7 },
        new Character { Id = 8, Name = "Violet Crawley", ProductionId = 5, ActorId = 8 },
        new Character { Id = 9, Name = "Lady Mary Crawley", ProductionId = 5, ActorId = 9 },
        // television
        new Character { Id = 10, Name = "Will Smith", ProductionId = 6, ActorId = 7},
        new Character { Id = 11, Name = "Hilary Banks", ProductionId = 6, ActorId = 10 },
        new Character { Id = 12, Name = "Violet Crawley", ProductionId = 7, ActorId = 8 },
        new Character { Id = 13, Name = "Lady Mary Crawley", ProductionId = 7, ActorId = 9 },
        new Character { Id = 14, Name = "Eleven", ProductionId = 8, ActorId = 11 },
        new Character { Id = 15, Name = "Lucas", ProductionId = 8, ActorId = 12 },
        new Character { Id = 16, Name = "Joyce Byers", ProductionId = 8, ActorId = 13 },
        new Character { Id = 17, Name = "Jim Hopper", ProductionId = 8, ActorId = 14 },
        new Character { Id = 18, Name = "Ametani Kantarou", ProductionId = 9, ActorId = 15},
        new Character { Id = 19, Name = "Sano Erika", ProductionId = 9, ActorId = 16 },
        new Character { Id = 20, Name = "Daryl Dixon", ProductionId = 10, ActorId = 17 },
        new Character { Id = 21, Name = "Michonne", ProductionId = 10, ActorId = 3 },
        new Character { Id = 22, Name = "Carol Peletier", ProductionId = 10, ActorId = 18 }
    });

    // actors
    modelBuilder.Entity<Actor>().HasData(new Actor[]
    {
        new Actor { Id = 1, Name = "Robert Downey Jr." },
        new Actor { Id = 2, Name = "Chris Evans" },
        new Actor { Id = 3, Name = "Danai Guira" },
        new Actor { Id = 4, Name = "Donald Glover" },
        new Actor { Id = 5, Name = "Beyoncé" },
        new Actor { Id = 6, Name = "Donny Yen" },
        new Actor { Id = 7, Name = "Will Smith" },
        new Actor { Id = 8, Name = "Maggie Smith" },
        new Actor { Id = 9, Name = "Michelle Dockery" },
        new Actor { Id = 10, Name = "Karyn Parsons" },
        new Actor { Id = 11, Name = "Millie Bobby Brown" },
        new Actor { Id = 12, Name = "Caleb McLaughlin" },
        new Actor { Id = 13, Name = "Winona Ryder"},
        new Actor { Id = 14, Name = "David Harbour" },
        new Actor { Id = 15, Name = "Matsuya Onoe" },
        new Actor { Id = 16, Name = "Hazuki Shimizu"},
        new Actor { Id = 17, Name = "Norman Reedus" },
        new Actor { Id = 18, Name = "Melissa McBride" }
    });

    // let's generate lots of ratings
    var random = new Random();
    var size = 100;
    var sources = new [] {
        "Internet",
        "Newspaper",
        "Magazine",
        "App"
    };

    var ratings = productions
        .SelectMany((production, index) => {

            return Enumerable
                .Range(index * 100 + 1, size - 1)
                .Select(id => new Rating {
                    Id = id,
                    ProductionId = production.Id,
                    Stars = random.Next(1, 6),
                    Source = sources[random.Next(0, 4)]
                }).ToList();
        });

    modelBuilder.Entity<Rating>().HasData(ratings);

    #endregion

    base.OnModelCreating(modelBuilder);
}

There are a few caveats to seeding data that developers should be aware of:

  1. All identifiers need to be specified, even if the entity identifiers are auto-generated via the schema.
  2. We need to specify TPH data per type. In this case, one set of data for the Movie entity, and another for the Series entity.
  3. Our data must satisfy the requirements of our schema, including non-null columns.

Database migrations

Up to this point, we should have a schema expressed through the EntertainmentDbContext that we can apply to a database, although we’ve yet to apply any changes to our database. In our case, we are using an SQLite database so that folks can get up and running quickly with few external dependencies. Let’s initialize our database. From the Terminal tool window in JetBrains Rider, let’s run the following command.

>  dotnet ef migrations add InitialCreate --project Entertainment

By executing the command, we will build our current EF Core project and create our initial migrations. Our IntialCreate migration should have a timestamp prefix. Since migrations are chronological, the prefix is an essential tool for making sense of the changes occurring to our schema.

We should have some additional files in our project under the Migrations folder. These files are source control friendly, allowing multiple individuals to work on the EntertainmentDbContext without fear of irreparable conflicts or lost work. Since the dotnet-ef CLI tool generates and manages these files, we should resist the urge to modify these files via the IDE.

We can apply our migrations with the following command:

> dotnet ef database update --project Entertainment

Executing the database update command will apply our migrations up to the latest.

Build started...
Build succeeded.
Applying migration '20201023150600_InitialCreate'.
Done.

We can use Rider’s database tool window to make sure that our migrations were applied correctly.

Additionally, we can ask Rider to diagram our database tables, showing the relationships that we modeled during the code-first design phase of this tutorial.

database diagram tool in Rider

To test that our data has been seeded, we can use Rider’s built-in SQL query console. One of our favorite features is database introspection, which suggest SQL joins according to the current schema in the dialect of the target database engine.

sql query console

LINQ queries

We’ve done a lot of work up to this point to get to the reason developers choose an ORM in the first place, writing queries. This section will explore common queries we will write when dealing with our domain. Let’s look at a list of questions we can answer with our data.

  • What are the highest-grossing movies?
  • What are the highest-rated productions?
  • What are the sources of ratings by count?
  • Series with the least number of episodes?
  • What actors play multiple characters?

Before we answer these exciting questions, let’s talk about LINQ, also known as Language-Integrated Query syntax. In particular, we will use the Select method to project or results as anonymous objects. While we could use the types we’ve created in our EntertainmentDbContext, it’s essential to our app’s performance to return results we will use in our code. Like many things in life, the way we use EF Core’s LINQ interface depends on many factors, and developers should consider their use case and develop accordingly.

Highest grossing movies

We talked about how both a Movie and a Series is a Production. We store each entity type within the same Productions table, which EF Core manages for us. We can access the Movie types in one of two ways.

The first approach is to use the DbSet on our EntertainmentDbContext class.

var movies = database
    .Movies
    .OrderByDescending(x => x.WorldwideBoxOfficeGross);

We can also use the OfType method after accessing the Productions property to narrow down to a particular hierarchical subset. In our case, we want Movies.

var movies = database
    .Productions.OfType<Movie>()
    .OrderByDescending(x => x.WorldwideBoxOfficeGross);

Let’s take a look at the generated SQL from our LINQ statement. EF Core comes with a ToQueryString extension method to apply to IQueryable instances to retrieve the generated SQL string.

 SELECT "p"."Id", "p"."Discriminator", "p"."Name", "p"."Release",
 "p"."DurationInMinutes", "p"."WorldwideBoxOfficeGross"
 FROM "Productions" AS "p"
 WHERE "p"."Discriminator" = 'Movie'
 ORDER BY "p"."WorldwideBoxOfficeGross" DESC

We can see that EF Core utilized the Discriminator column, even though there is no explicit mention of it in our database model.

Highest-rated productions

Aggregation is one of the most important reasons to use a relational database, and with LINQ, using an aggregate function like Sum, Average, and Max is straightforward. Let’s find the highest-rated production by averaging the Star value on a Rating.

var highestRated = database
    .Productions
    .Select(x => new
    {
        id = x.Id,
        name = x.Name,
        avg = x.Ratings.Average(r => r.Stars),
        type = x.GetType().Name
    })
    .OrderByDescending(x => x.avg);

As we can see, we utilize the navigation property of Ratings to determine the average of the star values. What does this translate to in SQL?

 SELECT "p"."Id", "p"."Name", (
     SELECT AVG(CAST("r"."Stars" AS REAL))
     FROM "Ratings" AS "r"
     WHERE "p"."Id" = "r"."ProductionId"), "p"."Discriminator", "p"."Release",
 "p"."DurationInMinutes", "p"."WorldwideBoxOfficeGross", "p"."NumberOfEpisodes"
 FROM "Productions" AS "p"
 ORDER BY (
     SELECT AVG(CAST("r0"."Stars" AS REAL))
     FROM "Ratings" AS "r0"
     WHERE "p"."Id" = "r0"."ProductionId") DESC

We can see that EF Core applies a sub-query to retrieve the average rating value of a Production. Additionally, it uses an additional sub-query to order our results.

EF Core users who want to write the query starting with the Ratings property cannot currently do so due to some EF Core query generator limitations. The EF team is still actively working through querying scenarios and are trying to resolve some of these issues. The issue appears when trying to select the production information from the rating’s row. In the next section, we’ll see that it’s possible to do aggregates on a single table without traversing the production relationship.

Sources of ratings by count

As mentioned in the previous section, aggregations are essential for providing summary data. In this example, we’ll look at the sources of ratings and how many exist in our database.

var sources = database
    .Ratings
    .GroupBy(x => x.Source)
    .Select(x => new { Source = x.Key, Count = x.Count() })
    .OrderByDescending(x => x.Count);

Unlike the previous query, this example only uses the Rating entity, which results in the following SQL.

SELECT "r"."Source", COUNT(*) AS "Count"
 FROM "Ratings" AS "r"
 GROUP BY "r"."Source"
 ORDER BY COUNT(*) DESC

Series with the fewest episodes

LINQ provides multiple approaches to pick the top record from a query, with methods like Take, First, and FirstOrDefault. In this example, let’s find the Series with the fewest number of episodes.

var episodes = database
    .Series
    .OrderBy(x => x.NumberOfEpisodes)
    .Select(x => new {
        x.Name,
        x.NumberOfEpisodes,
        x.Release
    })
    .Take(1);

An element to note in our LINQ query is the value of 1 passed to the Take method. This value exists in C# but does not exist in our SQL query unless we give it to our database engine. Let’s take a look at the generated SQL.

 .param set @__p_0 1

 SELECT "p"."Name", "p"."NumberOfEpisodes", "p"."Release"
 FROM "Productions" AS "p"
 WHERE "p"."Discriminator" = 'Series'
 ORDER BY "p"."NumberOfEpisodes"
 LIMIT @__p_0

As we can see, EF Core passes our value of 1 to the database as a parameter. Any constant we define in C# will be added as either a parameter or hardcoded into the SQL Query.

Here is an example where we find all productions with «The» in the name.

var productionsWithTheInName = database
    .Productions
    .Where(x => x.Name.Contains("The"));

The value of «The» finds its way into the generated SQL’s where clause.

 SELECT "p"."Id", "p"."Discriminator", "p"."Name", "p"."Release", "p"."DurationInMinutes", "p"."WorldwideBoxOfficeGross", "p"."NumberOfEpisodes"
 FROM "Productions" AS "p"
 WHERE ('The' = '') OR (instr("p"."Name", 'The') > 0)

Actors playing characters in multiple productions

Likely the most complex query in this list, let’s find actors playing characters in different productions. We’ll need to start at the Actors entity and navigate through the Characters collection while including the Production information.

var multipleCharacters = database
    .Actors
    .Where(a => a.Characters.Count > 1)
    .Select(a => new {
        a.Name, 
        // some characters are both in TV and Movies
        Characters = a.Characters.Select(x => new {
            Name = x.Name,
            ProductionType = x.Production.GetType().Name
        })
        .OrderBy(x => x.Name)
        .ToList()
    });

As you can imagine, the SQL generated by EF Core will reflect the complexity of our request.

 SELECT "a"."Name", "a"."Id", "t"."Name", "t"."Id", "t"."Discriminator", "t"."Name0", "t"."Release", "t"."DurationInMinutes", "t"."WorldwideBoxOfficeGross",
 "t"."NumberOfEpisodes", "t"."Id0"
 FROM "Actors" AS "a"
 LEFT JOIN (
     SELECT "c"."Name", "p"."Id", "p"."Discriminator", "p"."Name" AS "Name0", "p"."Release", "p"."DurationInMinutes", "p"."WorldwideBoxOfficeGross", "p"."NumberOfEpisodes", 
 "c"."Id" AS "Id0", "c"."ActorId"
     FROM "Characters" AS "c"
     INNER JOIN "Productions" AS "p" ON "c"."ProductionId" = "p"."Id"
 ) AS "t" ON "a"."Id" = "t"."ActorId"
 WHERE (
     SELECT COUNT(*)
     FROM "Characters" AS "c0"
     WHERE "a"."Id" = "c0"."ActorId") > 1
 ORDER BY "a"."Id", "t"."Name", "t"."Id0", "t"."Id"

The SQL statement is still very readable, even when we consider the answer we are returning from our database.

Conclusion

In this post, we modeled an entertainment problem domain using code-first design. In addition to modeling, we applied our schema to an SQLite database. Finally, we used the expressiveness of LINQ to generate SQL queries and return our result set.

From my previous experience with Entity Framework 6, I find the generated SQL of EF Core more readable and concise. EF Core still falters around some strange edge cases, but the EF team is actively working to support more complex LINQ to SQL translations. If you’re considering working with a database engine, I highly recommend looking at EF Core as an option to jumpstart your development.

You can play with these samples directly by cloning our sample repository.

Download Rider and give it a try!

Subscribe to Blog updates

Discover more

In October 2023, we’re hosting another JetBrains GameDev Day – a free, live, virtual event where community speakers cover topics they are passionate about. 

Last year, we invited seven experts from various areas to give talks, hold discussions, and share pro tips on how to craft the best games. The event attracted over 4,000 online attendees! This year, we want to repeat our success and make the experience even better for all.

In 2023, we’re going hybrid. The whole event will be online, but we’ll also be hosting viewers in Cyprus, so you can join us there to watch the livestream tog

The Complete Entity Framework Core Tutorial or EF Core Tutorial covers everything from creating an EF Core application, Connecting to a database, Creating Context & DbSet. Modeling the Database using Conventions, Data Annotations & Fluent API. Learn to Query, Insert, Update & Delete Records from the database using EF Core. This is a step by step guide to the EF Core Using C#. This tutorial is aimed at both beginners & professionals to learn all the features & concepts of the Entity Framework Core.

Table of Contents

  • Prerequisite
  • What is Entity Framework Core
  • Table of Content
  • 1. Getting Started
  • 2. Connecting to Database
  • 3. Configuring the Model
    • 3A. Configure Using Conventions
    • 3B. Configure Using Data Annotations
    • 3C. Configure Using Fluent API
  • 4. Configuring the Relationships
  • 5. Querying in EF Core
  • 6. Persisting the Data
  • 7. Migrations
  • 8. Others
  • 9. Resources

Prerequisite

  • C#
  • Visual Studio 2019
  • MS SQL Server and Querries

The Microsoft Entity Framework Core or EF Core is Microsoft’s implementation of  ORM Framework.  The applications created using the EF does not work with the database directly. The application works only with the API provided by the EF for database related operations. The EF maps those operations to the database.

Best Entity Framework Core Books
The Best EF Core Books, which helps you to get started with EF Core  

Table of Content

1. Getting Started

This Getting Started Tutorial guides you through the process of creating your first EF Core Application from scratch.

  1. Getting Started: Create your First Entity Framework Console Application 
  2. Installing EF Core

2. Connecting to Database

  1. Database Connection String
  2. DBContext
  3. DbSet

3. Configuring the Model

The Following Tutorials Explains how to Configure the Models using Convention, Data Annotations & Fluent API

3A. Configure Using Conventions

  1. Code First Conventions in Entity Framework Core 

3B. Configure Using Data Annotations

  1. Data Annotations in entity framework Core 
  2. Table Attribute
  3. Column Attribute
  4. Key Attribute
  5. ComplexType Attribute
  6. ConcurrencyCheck Attribute
  7. Timestamp Attribute
  8. Databasegenerated Attribute
  9. ForeignKey Attribute
  10. MaxLength / MinLength Attribute
  11. StringLength Attribute
  12. NotMapped Attribute
  13. Required Attribute
  14. InverseProperty Attribute

3C. Configure Using Fluent API

  1. Fluent API Entity Framework Core
  2. Ignore Method
  3. HasAlternateKey
  4. HasKey
  5. EntityType Configuration

4. Configuring the Relationships

Learn how to Configure the Relationships in Entity Framework  by using Navigation property and making use of Conventions, Data Annotations & Fluent API

  1. Relationships & Navigational Properties
  2. One to One Relationships
  3. One to Many Relationships
  4. Many To Many Relationships

5. Querying in EF Core

Querying in Entity Framework Core has not changed from its predecessor Entity Framework. The LINQ To entities is now the preferred way of Querying the Entities. The Following Articles introduces you to the LINQ to Entities in EF Core and shows how to Query, Find a Single Entry, Join two or more tables. Inner join & Left Joins, Select Query, sort & Filter etc.

  1. Querying in Entity Framework Core
  2. First, FirstOrDefault, Single, SingleOrDefault in EF Core
  3. EF Core Find Method
  4. Projection Queries in EF Core
  5. EF Core Join Query
  6. Eager Loading using Include & ThenInclude in EF Core
  7. SelectMany in EF Core
  8. Explicit Loading in EF Core
  9. Lazy Loading in EF Core

6. Persisting the Data

  1. Save Changes in Entity Framework Core
  2. ChangeTracker, EntityEntry & Entity States
  3. Add Records/Add Multiple Records
  4. Update Record
  5. Delete Record
  6. Cascade Delete in Entity Framework Core

7. Migrations

  1. EF Core Migrations
  2. Reverse Engineer Database (Database First)
  3. EF Core Data Seeding to the Database
  4. EF Core Script MIgration

8. Others

  1. Logging in EF Core
  2. Using MySQL & MariaDB in Entity Framework Core

9. Resources

  1. Best Books on Entity Framework Core

Понравилась статья? Поделить с друзьями:
  • Предоставление материнского дня в рб инструкция
  • Миостимулятор digital therapy machine st 688 инструкция на русском
  • Швейная машина бразер 35а комфорт инструкция по применению
  • Клей red sun инструкция на русском языке по применению
  • Aprilia futura rst 1000 мануал