Основы entity framework

DbSet.Add()

The method attaches the entire entity graph to a context and automatically applies the Added state to all entities.


//disconnected entity graph
Student disconnectedStudent = new Student() { StudentName = "New Student" };
disconnectedStudent.StudentAddress = new StudentAddress() { Address1 = "Address", City = "City1" };

using (var context = new SchoolDBEntities())
{
    context.Students.Add(disconnectedStudent);
                
    // get DbEntityEntry instance to check the EntityState of specified entity
    var studentEntry = context.Entry(disconnectedStudent);
    var addressEntry = context.Entry(disconnectedStudent.StudentAddress);

    Console.WriteLine("Student: {0}", studentEntry.State);
    Console.WriteLine("StudentAddress: {0}", addressEntry.State);
}

Output: Student: Added StudentAddress: Added

The method attaches the entire entity graph to a context with the Added state to each entity. Calling will execute the INSERT command for all the entities, which will insert new records in the appropriate database table.

Benefits

Entity Framework features:

Entity Framework Canonical Functions support

dotConnect data providers support most of the Entity Framework Canonical Functions for Oracle, MySQL, PostgreSQL, SQLite, and DB2.

Dynamic Database Creation and Deletion Support

dotConnect data providers support Dynamic database creation and database deletion for Entity Framework v1, v4.x, v5, and v6. They also support Code-First Migrations for Entity Framework 4.3 and higher.

Entity Framework Spatials Support  

dotConnect data providers for Oracle, MySQL, and PostgreSQL support Entity Framework Spatials functionality for Entity Framework v5 and v6.

Database-specific features:

Wide Support for Database Types

dotConnect product line offers wide support for database types including Oracle CURSORs and SDO_GEOMETRY, MySQL geometry, PostgreSQL PostGIS geometry and geography data types.

Wide Support for Database-specific Functions

Database-specific system functions of Oracle, MySQL, PostgreSQL, SQLite, and DB2 can be used in LINQ to Entities and Entity SQL.

Full-Text Search Suport

dotConnect for Oracle , MySQL , PostgreSQL , and SQLite support database full-text search functionality in LINQ to Entities and Entity SQL queries.

Performance:

Batch Updates

Our providers support configurable , grouping several INSERT/UPDATE/DELETE statements into one SQL block, which increases SaveChanges() performance substantially, because of reducing number of server calls and producing more compact SQL code.

Light and Optimized SQL

A number of SQL optimizations allows generating high-performant SQL. Additionally, you may optionally disable SQL formatting to reduce SQL statement size. dotConnect for Oracle supports Oracle Optimizer Hints for further performance improvements.

Eager Loading

Entity Framework Core supports eager loading of related entities, same as EF 6, using the extension method and projection query. In addition to this, it also provides the extension method to load multiple levels of related entities. (EF 6 does not support the method.)

Include

Unlike EF 6, we can specify a lambda expression as a parameter in the method to specify a navigation property as shown below.

var context = new SchoolContext();

var studentWithGrade = context.Students
                           .Where(s => s.FirstName == "Bill")
                           .Include(s => s.Grade)
                           .FirstOrDefault();

In the above example, passes the lambda expression to specify a reference property to be loaded with entity data from the database in a single SQL query. The above query executes the following SQL query in the database.

SELECT TOP(1) ., ., ., .,., 
        ., ., ., .
FROM  AS 
LEFT JOIN  AS  ON . = .
WHERE . = N'Bill'

We can also specify property name as a string in the method, same as in EF 6.

var context = new SchoolContext();

var studentWithGrade = context.Students
                        .Where(s => s.FirstName == "Bill")
                        .Include("Grade")
                        .FirstOrDefault();

The example above is not recommended because it will throw a runtime exception if a property name is misspelled or does not exist. Always use the method with a lambda expression, so that the error can be detected during compile time.

The extension method can also be used after the method, as shown below.

var context = new SchoolContext();

var studentWithGrade = context.Students
                        .FromSql("Select * from Students where FirstName ='Bill'")
                        .Include(s => s.Grade)
                        .FirstOrDefault();            

Note: The extension method cannot be used after the method. E.g. is not possible in EF Core 2.0. This may be possible in future versions.

Multiple Include

Use the method multiple times to load multiple navigation properties of the same entity. For example, the following code loads and related entities of .

var context = new SchoolContext();

var studentWithGrade = context.Students.Where(s => s.FirstName == "Bill")
                        .Include(s => s.Grade)
                        .Include(s => s.StudentCourses)
                        .FirstOrDefault();

The above query will execute two SQL queries in a single database round trip.

SELECT TOP(1) ., ., ., ., ., 
        ., ., ., .
FROM  AS 
LEFT JOIN  AS  ON . = .
WHERE . = N'Bill'
ORDER BY .
Go

SELECT ., .
FROM  AS 
INNER JOIN (
    SELECT DISTINCT .*
    FROM (
        SELECT TOP(1) .
        FROM  AS 
        LEFT JOIN  AS  ON . = .
        WHERE . = N'Bill'
        ORDER BY .
    ) AS 
) AS  ON . = .
ORDER BY .
Go

ThenInclude

EF Core introduced the new extension method to load multiple levels of related entities. Consider the following example:

var context = new SchoolContext();

var student = context.Students.Where(s => s.FirstName == "Bill")
                        .Include(s => s.Grade)
                            .ThenInclude(g => g.Teachers)
                        .FirstOrDefault();

In the above example, will load the reference navigation property of the entity. will load the collection property of the entity. The method must be called after the method. The above will execute the following SQL queries in the database.

SELECT TOP(1) ., ., ., ., .,
         ., ., ., .
FROM  AS 
LEFT JOIN  AS  ON . = .
WHERE . = N'Bill'
ORDER BY .
Go

SELECT ., ., .
FROM  AS 
INNER JOIN (
    SELECT DISTINCT .*
    FROM (
        SELECT TOP(1) .
        FROM  AS 
        LEFT JOIN  AS  ON . = .
        WHERE . = N'Bill'
        ORDER BY .
    ) AS 
) AS  ON . = .
ORDER BY .
go

One-to-One Relationship

As you can see in the above figure, and have a One-to-One relationship (zero or one). A student can have only one or zero addresses. Entity framework adds the into the entity and the navigation entity into the entity. Also, the entity has both property as PrimaryKey and ForeignKey, which makes it a one-to-one relationship.

public partial class Student
{
    public Student()
    {
        this.Courses = new HashSet<Course>();
    }
    
    public int StudentID { get; set; }
    public string StudentName { get; set; }
    public Nullable<int> StandardId { get; set; }
    public byte[] RowVersion { get; set; }
    
    public virtual StudentAddress StudentAddress { get; set; }
    }
    
public partial class StudentAddress
{
    public int StudentID { get; set; }
    public string Address1 { get; set; }
    public string Address2 { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    
    public virtual Student Student { get; set; }
}

In the above example, the property needs to be PrimaryKey as well as ForeignKey. This can be configured using Fluent API in the method of the context class.

Подход Code-First

Вы можете использовать этот класс модели в своем приложении без создания базы данных. Добавив дополнительный код, вы могли бы хранить объекты этого класса, например, на диске в формате XML или в памяти рабочего процесса программы. Однако когда ваше приложение расширяется и становится крупным, эти данные необходимо будет хранить в базе данных

Эта та точка, в которой и начинает работать подход Code-First – вы можете использовать существующий код для создания базы данных не беспокоясь о деталях реализации базы данных (этим займется Entity Framework), а вы можете сфокусировать свое внимание на коде

Важно отметить, что класс, представляющий модель данных, должен иметь поле Id, которое будет использоваться в таблице базы данных в качестве первичного ключа. Entity Framework автоматически находит такое поле с помощью механизма рефлексии (в его имени должна содержаться строка “Id”, поэтому поле CustomerId в примере выше будет автоматически использоваться в качестве первичного ключа.) Это ограничение можно обойти, использовав свойство с произвольным именем и помеченное специальными атрибутами метаданных C#, которые используются в Entity Framework

Здесь мы их описывать не будем, я лишь заострил свое внимание на этом моменте, потому что он показывает, что любые настройки первичных и внешних ключей, ограничения между таблицами и т.д. при подходе Code-First указываются в модели (т.е

в управляемом коде C#), а потом проецируются на базу данных.

Подход Code-First появился позже подходов Model-First и Database-First и, как вы уже поняли, больше всего подходит для разработчиков, которые хотят писать код, а не работать с дизайнером модели EDM или средствами работы с базами данных (SQL Server Management Studio и T-SQL). Вы можете создать модель для вашего приложения, используя объекты CLR (Common Language Runtime) и специальные объекты POCO (Plain Old CLR Object).

При проектировании приложений с подходом Code-First, вы сначала создаете классы модели данных не обращая никакого внимания на Entity Framework. После того, как вам понадобилось работать с базой данных, вы используете различные инструменты, которые проецируют структуру базы данных из созданной модели классов. После этого вы можете вернуться к этой модели в коде и, например, изменить ее. Эти изменения затем можно будет отразить в базе данных используя все те же инструменты.


Важным нововведением версии Entity Framework 5 в плане подхода Code-First, является то, что созданная модель классов теперь сразу является сущностной моделью данных EDM (Entity Data Model), поэтому отпала необходимость использовать файл EDMX. В более ранних версиях разработчику, использующему подход Code-First, приходилось добавлять отношения между моделью классов и файлом EDMX, т.е. отображать любые изменения модели сразу в двух местах. Очевидно, что этот подход приводил к появлению кучи ошибок, если разработчик забывал синхронизировать эти изменения в обоих файлах.

Чтобы указать среде Visual Studio, что модель классов является моделью EDM, нужно во-первых установить сборки Entity Framework в проект, а во-вторых добавить класс контекста базы данных, унаследованный от класса DbContext, находящегося в пространстве имен System.Data.Entity, как показано в примере ниже (установку Entity Framework и настройку класса контекста мы более подробно будем рассматривать позже):

Вам не нужно беспокоится о способах взаимодействия с базой данных, Entity Framework определяет ряд вспомогательных методов. Вам не нужно знать деталей подключения и даже имени базы данных. Ниже показан пример вставки данных в таблицу с использованием тестового класса Customer, показанного выше:

Этот код является немного упрощенным, но все же показывает суть работы с Entity Framework. Конечно, вы можете глубже контролировать каждую операцию с базой данных. Этот пример использует максимальную автоматизацию и дает приемлемые результаты, по крайней мере в небольшом приложении. В последующих статьях мы рассмотрим более подробно настройки подхода Code-First, которые можно использовать для удовлетворения конкретных потребностей.

Extensions of EDM

Features Entity Developer EDM designer
Advanced mapping support with all kinds of inheritances
Database-First approach support with preserving manual mapping and storage changes while updating a model from a database
Model-First approach support with synchronization of storage part and database with the model
Advanced large model support splitting model into several diagrams
Robust and easy-to-use drag ‘n’ drop mechanism
Editable storage model
Advanced CUD-mapping with stored functions support
Function import and code generation with support for stored routines, returning entity collections and collections of complex types
Background design-time model validating and testing the model with Entity SQL and LINQ to Entities queries
Code generation with support for design-time custom attributes of model objects

* Some of the advanced features are available only in Entity Developer

** EDM designer supports this functionality since Visual Studio 2012.

Visit our Entity Developer vs Entity Data Model Designer page to get more information on Entity Developer benefits.

Связь модели с базой данных

К данному моменту мы создали простую модель, которую можем применить в нашем приложении, но мы не привязали ее к реальной базе данных. Чтобы убедиться в этом, щелкните правой кнопкой мыши по объекту Customer и выберите в контекстном меню пункт Validate. Visual Studio отобразит ошибку в окне ErrorList:

Error 11007: Entity type 'Customer' is not mapped.	

Итак, нам нужно связать созданную модель с физической базой данных. Т.к. базы данных еще не существует на SQL Server, ее можно будет сгенерировать из нашей модели с использованиям средств Visual Studio (это еще одна сторона процесса разработки с использованием Model-First – сначала мы создали модель, а затем, на ее основе создаем базу данных). Для этого выполните следующие шаги:

Щелкните правой кнопкой мыши по объекту Customer в дизайнере EDM и выберите пункт Generate Database From Model (Создать базу данных из модели). Откроется диалоговое окно, показанное на рисунке ниже: Щелкните по кнопке New Connection (Новое подключение). Откроется диалоговое окно Choose Data Source (Выбор источника данных), выберите Microsoft SQL Server

В открывшемся окне Connection Properties (Свойства подключения) введите имя сервера к которому необходимо выполнить подключение и введите имя базы данных TestCustomer:

Обратите внимание, что если в этом окне вы нажмете кнопку Test Connection, чтобы проверить работает ли подключение к базе данных, то вылезет ошибка подключения, т.к. базы данных TestCustomer еще не существует. Нажмите кнопку OK, вы увидите диалоговое окно, сообщающее вам что база данных не существует и Visual Studio требует разрешение на ее создание. Нажмите кнопку Yes, Visual Studio создаст новую базу данных

В данный момент эта база данных пуста, в ней отсутствуют таблицы, индексы, представления и другие объекты. После этого вы вернетесь в окно Generate Database From Model. Если на данном этапе возникли проблемы с подключением или авторизацией SQL Server, базу данных можно изначально создать с помощью SQL Management Studio, а затем повторить шаги 1-4, указав в окне Connection Properties имя уже имеющейся базы данных. Нажмите кнопку Next (Далее) в окне Generate Database From Model. Мастер создаст скрипт на языке DDL — Data Definition Language (используется синтаксис T-SQL), в котором происходит создание таблиц базы данных. Нажмите кнопку Finish (Готово). Visual Studio создаст файл Model1.edmx.sql, содержащий SQL-инструкции с использованием семантики DDL. Выполните этот скрипт, нажав на кнопке Execute (имеет вид зеленого треугольника на панели инструментов) или выполнив команды меню SQL —> Transact SQL Editor —> Execute. Вы увидите диалоговое окно, в котором нужно выбрать подключение к серверу SQL Server, на котором выполнится данный скрипт: Введите все необходимые учетные данные и нажмите Connect (Подключиться). Среда Visual Studio подключится к базе данных и выполнит сценарий SQL, который она создала. В этот момент, база данных готова к использованию.

Обратите внимание как Visual Studio облегчает вам жизнь – не нужно вручную работать с базами данных и придумывать SQL-код для создания таблиц, нужно сделать всего пару кликов в дизайнере EDM и настроить подключение при создании базы данных

Явная загрузка (explicit loading)

Последним вариантом загрузки данных в Entity Framework является явная загрузка (explicit loading) данных. Явная загрузка, как и отложенная загрузка, не приводит к загрузке всех связанных данных в первом запросе. Но при этом, в отличие от отложенной загрузки, при вызове навигационного свойства связанного класса, эта загрузка не приводит к автоматическому извлечению связанных данных, вы должны явно вызвать метод Load(), если хотите загрузить связанные данные. Такой тип загрузки может использоваться в следующих случаях:

  • Этот тип загрузки устраняет необходимость отмечать навигационные свойства класса модели как виртуальные. Для вас это может показаться не значительным изменением, но тот факт, что технология доступа к связанным данным при отложенной загрузке требует изменять ваши классы POCO модели, далека от идеальной.

  • Вы можете работать с существующей библиотекой классов, где навигационные свойства не помечены как виртуальные, и вы не можете изменить эту библиотеку.

  • В конце концов явная загрузка позволяет быть уверенным, что вы точно знаете, когда запросы отправляются в базу данных. Отложенная загрузка имеет характерную особенность генерировать много запросов к базе данных, с явной загрузкой очевидно, когда и где запросы выполняются в настоящее время.

Явная загрузка использует метод DbContext.Entry() для доступа к сущностному объекту, а не свойство типа DbSet. Объект DbEntityEntry, возвращаемый этим методом, дает вам доступ ко всей информации о сущностном типе данных. Помимо обычных свойств модели, этот объект хранит большое количество расширенных настроек сущностных объектов, а также позволяет вызвать метод Load() для вызова явной загрузки. В примере ниже мы используем метод ExplicitLoading(), в котором мы реализовали самый первый пример в этой статье, в котором мы не могли загрузить данные заказов автоматически, т.к. навигационное свойство Customer.Orders не было виртуальным:


В первой части этого примера мы загружаем данные пользователя с идентификатором, равным 2. Затем мы используем явную загрузку, чтобы указать Entity Framework о необходимости загрузить для пользователя связанные с ним заказы

Обратите внимание, что в этом примере используется метод Collection(), которому передается делегат, в котором выбирается навигационное свойство, имеющее тип коллекции. Если навигационное свойство имеет тип ссылки (например, для связи один-к-одному между таблицами), то нужно использовать метод Reference()

После этого используется метод Load() для создания запроса к базе данных.

Если вы теперь вызовете этот метод в основном методе Main() и запустите приложение, то сможете убедиться, что заказы извлекаются корректно. Стоит отметить, что в этом примере будет создано два SQL-запроса к базе данных: при извлечения данных покупателя с CustomerId = 2 и при загрузке заказов для этого покупателя.

Методы Collection() и Reference() класса DbEntityEntry возвращают экземпляры классов DbCollectionEntry и DbReferenceEntry. Эти классы содержат полезное свойство IsLoaded, которое указывает, были ли уже загружены ранее связанные данные. Это свойство позволит оптимизировать вам некоторые запросы, чтобы повторно не извлекать соответствующие данные и его можно использовать не только при явной загрузке, но и при других типах загрузок. Ниже показан соответствующий пример:

В предыдущих примерах при использовании явной загрузки мы указывали, что нужно выбрать все заказы. Что делать, если нужно использовать LINQ-методы для ограничения этой выборки, ведь классы DbCollectionEntry и DbReferenceEntry, объекты которых возвращаются методами Collection() и Reference(), не подходят для использования LINQ-запросов? Для этих целей в этих классах определен вспомогательный метод Query(), который возвращает типизированную сущностным классом коллекцию IQueryable. В примере ниже показано использование этого метода, для ограничения заказов, если количество товаров меньше 5:

Version History

Version Downloads Last updated
6.4.4 802,904 5/12/2020
6.4.0 2,915,672 12/3/2019
6.4.0-preview3-19553-01 22,307 11/13/2019
6.4.0-preview2-19525-03 8,101 11/1/2019
6.4.0-preview1-19506-01 17,886 10/15/2019
6.3.0 2,520,411 9/23/2019
6.3.0-rc1-19458-04 4,371 9/16/2019
6.3.0-preview9-19423-04 8,378 9/4/2019
6.3.0-preview8-19405-04 13,083 8/13/2019
6.3.0-preview7-19363-02 27,067 7/23/2019
6.3.0-preview6-19304-03 29,933 6/12/2019
6.3.0-preview5-19254-05 22,668 5/7/2019
6.2.0 23,078,062 10/26/2017
6.2.0-beta1 199,511 5/19/2017
6.1.3 32,921,261 3/10/2015
6.1.3-beta1 202,097 2/11/2015
6.1.2 2,470,072 12/22/2014
6.1.2-beta2 74,446 11/14/2014
6.1.2-beta1 147,371 9/18/2014
6.1.1 4,874,226 6/20/2014
6.1.1-beta1 74,484 5/20/2014
6.1.0 4,267,291 3/17/2014
6.1.0-beta1 98,698 2/11/2014
6.1.0-alpha1 85,738 12/20/2013
6.0.2 2,255,777 12/13/2013
6.0.2-beta1 74,358 11/27/2013
6.0.1 1,349,253 10/17/2013
6.0.0 6,064,986 10/17/2013
6.0.0-rc1 114,032 8/21/2013
6.0.0-beta1 124,730 5/28/2013
6.0.0-alpha3 79,701 2/25/2013
6.0.0-alpha2 73,212 12/10/2012
6.0.0-alpha1 57,157 10/25/2012
5.0.0 10,230,689 8/11/2012
5.0.0-rc 184,877 5/15/2012
5.0.0-beta2 36,926 3/22/2012
5.0.0-beta1 40,519 2/29/2012
4.3.1 614,865 2/29/2012
4.3.0 134,702 2/9/2012
4.3.0-beta1 23,371 1/13/2012
4.2.0 473,592 11/1/2011
4.1.10715 849,083 7/25/2011
4.1.10331 903,371 4/12/2011

С этим читают