Entity Framework — не нужен?

Не так давно я услышал мнение, что "Если надолго, надежно и с поддержкой - это Entity Framework. Если по-быстрому накидать что-нибудь простое - то ADO.NET". Времени обсуждать, почему человек так считает, не было, но его точка зрения мне запомнилась, потому что я это видел несколько иначе.

Эрик Эванс в своей книге "Предметно-ориентированное проектирование" упоминал, что разработчики нередко думают об объектах приложения в терминах таблиц баз данных. Вот у нас есть условный "Заказ", стало быть есть и таблица заказ, есть "Поставщик", значит и таблица такая. В итоге модель приложения ("домен") сливается со структурой базы данных и если вдруг приходит идея\необходимость разместить данные как-то иначе, это напрямую затрагивает модель, что как минимум нежелательно, а строго говоря - плохо.

Ведь тот самый условный объект "Заказ", который мы создаем в приложении и который в нем является целостным, может быть удобно "размазать" по нескольким таблицам БД, а потом, когда он снова понадобится, восстановить из этих нескольких таблиц.

Пример: класс Заказ (Order) содержит идентификатор, дату создания, еще что-нибудь (неважно) и компанию-получателя. Компания-получатель (Client) это тоже отдельный класс с идентификатором, названием и какими-нибудь реквизитами. Соответственно, в БД это будет скорее всего храниться так: таблица "Заказ" и таблица "Компания". В таблице "Заказ" в поле "Получатель" будет идентификатор компании из таблицы "Компания". В приложении же куда логичнее было бы, чтобы в объекте Order было поле типа Client, в котором лежит объект компании-получателя, а не условный int с идентификатором.

Так вот этот подход "Единый программный объект <--> Представление-в-БД-такое-какое-сейчас-удобнее", насколько мне видится, легко реализуется через обычное ADO.NET. Мы просто пишем слой доступа к данным, в котором классическим SQL выбираем нужные данные из скольких угодно таблиц, восстанавливаем объект Client и Order, вкладывая первый во второй, и все - готово. Просто и наглядно. Модель остается изолированной, а как она будет храниться - можно хоть сто раз переделать прозрачным для нее образом.

В случае же с EF я не вижу как такое можно сделать. Все примеры, которые я встречал в интернете демонстрируют подход, когда объект EF - один в один таблица БД. То есть получается объект Order с тем самым пресловутым int в качестве компании-получателя.

Что в итоге получается, что как раз ADO - это для "серьезно и надолго", а EF - "для простых случаев, чтобы не возиться с запросами, когда объект приложения - один в один таблица БД"? Или где я что-то пропустил или неправильно понял?
  • Вопрос задан
  • 209 просмотров
Решения вопроса 2
@Free_ze
Пишу комментарии в комментарии, а не в ответы
EF вам дает возможность автоматически транслировать экспрешны в диалекты SQL и маппить это на удобные дотнетные сущности. С голым ADO.NET вы будете делать то же самое, но руками.

куда логичнее было бы, чтобы в объекте Order было поле типа Client, в котором лежит объект компании-получателя, а не условный int с идентификатором.

Предлагаемая вами схема денормализована, вообще говоря, это не очень хорошо. Но посмотрите на Owned Types для EF Core и на Complex Types для EF6, мне кажется, что это то, что вы хотели бы - "встраивать" один объект в другой, сохраняя всё в единой таблице.

Да, EF не то, чтобы позволяет сильно отходить от классической реляционной модели, но с ним можно точечно звать raw sql-queries, что даст больше гибкости там, где это необходимо.
Ответ написан
firedragon
@firedragon
Senior .NET developer
Если по простому то так. Всю грязную работу сделает за вас EF.
Тут присутствуют 3 таблицы, контроль целостности и индексы для поиска.
Что же до ADO или там Daper честно голова болит портянки sql править.
Правда это не отменяет возможности пнуть приложение в дальнейшем, создав высокоскоростной адаптер для какой нибудь сущности.

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace HeIsBad.Dbl.Models
{
    /// <summary>
    /// Жалоба
    /// </summary>
    [Table("blames")]
    public class DbBlame
    {
        /// <summary>
        /// Идентификатор
        /// </summary>
        [Key]
        public long Id { get; set; }
        /// <summary>
        /// Жалоба
        /// </summary>
        [Required]
        public string Body { get; set; }
        /// <summary>
        /// Дата создания
        /// </summary>
        public DateTime Created { get; set; }
        /// <summary>
        /// Электронная почта жалобщика
        /// </summary>
        [Required]
        public string Email { get; set; }
        /// <summary>
        /// Причина жалобы
        /// </summary>
        [Required]
        [ForeignKey ("Reason")]  
        public int ReasonId { get; set; }

        
        public virtual DbBlameReason Reason { get; set; }
        /// <summary>
        /// Адрес жалобы
        /// </summary>
        public string BlameUri { get; set; }
        /// <summary>
        /// Кем создано. 
        /// </summary>
        public long? UserId { get; set; }
        
        /// <summary>
        /// Статус жалобы
        /// </summary>
        [ForeignKey ("BlameStatus")]   
        public int StatusId { get; set; }
        public virtual BlameStatus BlameStatus { get; set; }
    }
}
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@ddd329
Приветствую!

Эрик Эванс в своей книге "Предметно-ориентированное проектирование" упоминал, что разработчики нередко думают об объектах приложения в терминах таблиц баз данных. Вот у нас есть условный "Заказ", стало быть есть и таблица заказ, есть "Поставщик", значит и таблица такая. В итоге модель приложения ("домен") сливается со структурой базы данных и если вдруг приходит идея\необходимость разместить данные как-то иначе, это напрямую затрагивает модель, что как минимум нежелательно, а строго говоря - плохо.


Да, Эванс писал что-то подобное, но основной акцент у него был сделан на том, что связи, которые существуют между таблицами в БД, разработчики переносят их в модель, тем самым все усложняя.
И, приведенный вами пример, как раз это очень хорошо демонстрирует! Не нужна вам объектная ссылка между Client и Order, т.к. это разные агрегаты и каждый из них реализует свои инварианты. А вот ссылка между агрегатами в виде идентификаторов - int, Guid или чего-то другого, как раз является рекомендуемым способом.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы