Почему Entity Framework работает быстрее, чем Dapper в выражении прямого выбора

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

Итак, я сделал 2 простых проекта-прототипа, один из которых использует Dapper, а другой использует Entity Framework с одной функцией, чтобы получить все строки из одной таблицы. Схема таблицы как на следующем рисунке

Table Schema

и код для обоих проектов, как показано ниже

для Dapper проекта

System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
sw.Start();
IEnumerable<Emp> emplist = cn.Query<Emp>(@"Select * From Employees");
sw.Stop();
MessageBox.Show(sw.ElapsedMilliseconds.ToString());

для Entity Framework Project

System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
sw.Start();
IEnumerable<Employee> emplist = hrctx.Employees.ToList();
sw.Stop();
MessageBox.Show(sw.ElapsedMilliseconds.ToString());

после многократной попытки вышеприведенного кода только при первом запуске проекта, более щадящий код будет быстрее, и после этого первого раза всегда я получаю лучшие результаты из проекта Entity Framework. Я также попробовал следующее утверждение в проекте Entity Framework, чтобы остановить ленивый погрузка

hrctx.Configuration.LazyLoadingEnabled = false;

но тот же EF работает быстрее, за исключением первого раза.

Может ли кто-нибудь дать мне объяснение или руководство о том, что делает EF быстрее в этом примере, хотя все статьи в Интернете говорят об обратном

Обновить

Я изменил строку кода в образце сущности, чтобы

IEnumerable<Employee> emplist = hrctx.Employees.AsNoTracking().ToList();

использование AsNoTracking, как упоминалось в некоторых статьях, останавливает кеширование структуры сущностей, и после остановки кеширования более щадящий образец работает лучше (но не очень большая разница)

Ответ 1

ORM (Object Relational Mapper) - это инструмент, который создает слой между вашим приложением и источником данных и возвращает вам реляционные объекты вместо (с точки зрения используемого вами С#) объектов ADO.NET. Это основная вещь, которую делает каждый ORM.

Для этого ORM обычно выполняют запрос и отображают возвращенный DataReader в класс POCO. Dapper ограничен здесь.

Чтобы расширить это, некоторые ORM (также называемые "полным ORM") делают гораздо больше, например, генерируя запрос, чтобы сделать вашу базу данных приложения независимой, кэшировать данные для будущих вызовов, управлять единицей работы для вас и многое другое. Все это хорошие инструменты и добавляет ценность ORM; но это идет с ценой. Entity Framework попадает в этот класс.

Чтобы сгенерировать запрос, EF должен выполнить дополнительный код. Кэш повышает производительность, но управление кешем требует выполнения дополнительного кода. То же самое относится к единице работы и любой другой дополнительной функции, предоставляемой EF. Все это избавляет вас от написания дополнительного кода, а EF оплачивает стоимость.

И стоимость это производительность. Поскольку Даппер выполняет очень простую работу, он работает быстрее; но вы должны написать больше кода. Поскольку EF делает намного больше, он (немного) медленнее; но вы должны написать меньше кода.

Так почему ваши тесты показывают противоположные результаты?
Потому что выполняемые вами тесты несопоставимы.

Полные ORM имеют много хороших функций, как описано выше; Одним из них является UnitOfWork. Отслеживание является одной из обязанностей UoW. Когда объект запрашивается (SQL-запрос) в первый раз, он вызывает обращение к базе данных. Этот объект затем сохраняется в кэш-памяти. Полная ORM отслеживает изменения, внесенные в этот уже загруженный объект (ы). Если тот же объект запрашивается снова (другой запрос SQL в той же области действия UoW, который включает загруженный объект), они не выполняют обход базы данных. Вместо этого они возвращают объект из кеша памяти. Таким образом, значительное время экономится.
Dapper не поддерживает эту функцию, которая заставляет его работать медленнее в ваших тестах.

Но это преимущество применимо, только если один и тот же объект загружен несколько раз. Кроме того, если количество объектов, загруженных в память, слишком велико, это приведет к замедлению полной ORM, поскольку тогда время, необходимое для проверки объектов в памяти, будет больше. Опять же, это преимущество зависит от варианта использования.

Ответ 3

Нет проблем смешать их вместе. В моем текущем проекте я использую Dapper для выбора данных и EF для создания и обновления и миграции баз данных.

Dapper становится чрезвычайно полезным, когда речь идет о сложных запросах, где задействовано более двух таблиц или когда есть несколько сложных операций (объединение более чем в один столбец, объединение с предложениями> = и <=, рекурсивные выборки, cte и т.д.), Где использовать чистый SQL намного проще, чем LINQ. Как я знаю, Entity Framework (в отличие от Dapper) не может использовать метод .FromSql() в пользовательских DTO. Он может отображать только одну таблицу, которая должна быть в контексте вашей базы данных.

Ответ 4

I read many articles which says that Dapper is faster than Entity Framework

Проблема большинства тестов в Интернете заключается в том, что они сравнивают EF Linq с Dapper. И это то, что ты тоже сделал. Что несправедливо. Автоматически сгенерированный запрос (EF) часто не совпадает с запросом, написанным хорошим разработчиком.

Это,

IEnumerable<Employee> emplist = hrctx.Employees.ToList();

должен быть заменен этим.

IEnumerable<Employee> emplist = hrctx.Employees.FromSql(@"Select * From Employees").AsNoTracking();

Редактировать:

Как указывает @mjwills, ниже приведена таблица результатов для операторов insert, update и select.

enter image description here

Dapper превосходит EF Core 2. Однако видно, что для простых запросов EF разница очень минимальна. Я разместил полную информацию здесь.