Предыстория


А ведь много, много пишут на Entity Framework. Фактически, это ORM «по умолчанию» для .NET и среды Microsoft Visual Studio…

Так, однажды, не очень давно, попал в мои руки один немаленький проект. Ко времени моего появления, проекту было примерно три года. В техническом отношении он представлял собой душераздирающее зрелище. Грубый замер показал, что веб-приложение легко и совершенно неоправданно умудрялось потреблять около 1ГБ серверной памяти на одновременного пользователя. Пришлось быстро-быстро разбираться, чего же там такого интересного написано. Хороший урок, как делать не надо.

В этом проекте для доступа к данным в реляционной БД применялся Entity Framework. Кроме очевидных архитектурных и программистских глупостей, навроде ToList() по любому поводу, Include чему попало, наложения ограничений на множества объектов не на уровне БД, а на уровне приложения с использованием LINQ To Objects, были и проблемы, связанные только с Entity Framework.

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

Семь раз отмерь или ужасы проектирования


CodeFirst и DBFirst не предлагать. И вот почему. Ответ здесь и философский, и практический.

CodeFirst. Безусловно, он даёт самые продвинутые возможности в Entity Framework и полный контроль. Некоторые готовы спорить за этот подход до хрипоты и до драки. Мой аргумент прост: пока у вас 5-10-15 сущностей, вы можете держать структуру системы в голове. Пользуйтесь на здоровье! Когда счёт идёт на сотни, вы этого уже не можете. У меня действительно легко идёт на сотни и в мою голову столько не помещается. В головы товарищей тоже, особенно когда вводишь в проект нового человека. Поэтому мне нужны картинки. Компактные, наглядные, удобные. Причем модель должна быть не на одной картинке, а на нескольких, по-частям, выбранным как-то предметно. Ну, и далее, с картинок что-то как-то должно генерироваться, не буду же я писать однотипный код сотнями раз, да ещё и не делать при этом ошибок.

Может, DBFirst? Не-ет, это тоже не для меня. Наследование я как буду делать? И потом, как я буду думать про прикладное через специфику базы? Там и типы данных свои. Если я написал, скажем, VARBINARY, мне придётся в голове помнить, что где-то это картинка, а где-то какой-нибудь хеш. Ну и документировать это отдельно, либо разводить фольклор с товарищами. Помножим это на 300 с лишком сущностей. Нет, это неудобно. Нет, хочу думать в прикладных типах. И потом, что останется после меня?

Остаётся ModelFirst. Здесь я хотя бы могу рисовать сущности приближенно к тому, что я потом получу в C#, и использовать .NET-ные типы для полей. Но и тут заботливо расставлены развесистые грабли:
  1. Дизайнер. Почему связи на нём скачут как попало? И проводятся по диаграмме, как нравится Visual Studio, а не как мне, причём в отдельных случаях завязываются узлами? Я хочу, чтобы они были прибиты к листу гвоздями и не двигались никуда без моего ведома, а то я их найти не могу после очередного открытия диаграммы. Кажется, я понимаю, почему так популярен CodeFirst.
  2. Как просто разрезать модель на несколько диаграмм? Ну, неудобно мне 300 сущностей на одной диаграмме и, кроме того, я замучиваюсь тыркать мышкою в ползунки. Да и очень тормозит дизайнер при таких количествах сущностей. Конечно, есть функция «Move to new diagram», но как же потом, если понадобится, обратно объединить диаграммы, может передвинуть часть сущностей, использовать одни и те же сущности на нескольких диаграммах?
    Гуру говорят, есть какой-то способ, есть. Он из области ручного редактирования .diagram файла. Но это нужно познать Высший замысел. Но я ж не гуру, мне просветляться некогда, мне прикладной функционал надо набирать.
  3. Вот, к примеру, нарисовал я поле типа string. В базу оно генерируется по умолчанию как nvarchar(max). Отвесить бы придумавшему это умолчание инженеру щедрый щелбан. Только за это умолчание. От души. В свойствах поля EntitySet изменить тип нельзя даже для одного случая. А я вообще хочу изменить это умолчание, чтобы все строковые поля генерировались другим типом БД. Что делать? Править в текстовом редакторе edmx для всех, а затем разгребать последующие глюки?
  4. Я по-прежнему не могу рисовать, используя свои прикладные типы, выбор типов ограничен в дизайнере. Что делать, если я придумал в процессе проектирования свой прикладной тип (или уже имею готовый) и просто хочу объяснить ORM, как он отображается в базу, а как — в код на C#? Ну, разумеется, мне при реализации типа придётся соблюсти некие правила, например, наличие implicit-преобразований к нативному типу, или реализация древнючего IConvertible во что-то.
  5. Что мне делать, если я хочу, чтобы код на C#, соответствующий сущностям, содержал что-нибудь особенное? А предлагается в таком случае вернуться к CodeFirst. Либо пожалуйте в дивный, новый мир T4.
  6. Надизайненное в редакторе наследование не развязывается в «человеческий» TPC (отдельная таблица под каждый класс со всеми полями), а только как:
    — TPT (отдельная таблица под каждый класс только со своими полями + связь к предку). Потом при выборке будет вам: JOINы под каждую связь наследования — беда с производительностью и головная боль при попытке сформировать ограничение на голимом SQL;
    — TPH (все уровни положить в одну таблицу, наплевав на нормализацию и другие чистые идеалы). Интересно, как вообще можно до такого додуматься.
    Если вы всё-таки хотите TPC – опять см. CodeFirst + ModelBinder. Пользуясь случаем, передаю привет тому самому инженеру. Отсыпьте ему жирных щелбанов по числу прикладных сущностей.

Чтобы понять, как «удобно» проектировать, советую поговорить с кем-то, кто пытается приспособить средства проектирования Entity Framework, скажем, под Oracle. Особенно внесение изменений в имеющуюся модель. Рекомендую найти для этой цели лицо противоположного пола. Контраст мировосприятий придаст остроту вашей дискуссии.

Где ключи от танка или «занимательное» программирование


Ну, давайте посмотрим, как же это программируется.
Начнём с простого: создадим и сохраним что-нибудь:
CDLIBEntities dbctx = new CDLIBEntities();            
 
//Country ctr1 = dbctx.Country.Create();
Country ctr1 = new Country();
ctr1.primaryKey = Guid.NewGuid(); //Ах, да! Надо не забыть вручную сгенерировать первичный ключ.
ctr1.Name = "Greece";
 
//Publisher pblshr1 = dbctx.Publisher.Create();
Publisher pblshr1 = new Publisher();
pblshr1.primaryKey = Guid.NewGuid(); //Ах, да! Опять сгенерировать ключ...
pblshr1.Name = "First Publisher";
pblshr1.Country = ctr1.primaryKey; //Чёрт, я опять забыл, надо, надо помнить о ключах и везде расставлять вручную, иначе EF подумает, что нарушилась ссылочная целостность
pblshr1.Country1 = ctr1;
 
//dbctx.Publisher.Attach(pblshr1); //АУ! Почему я вообще должен здесь заботиться о том, какого типа у меня сущность??? Ведь итак понятно, какого она типа.
dbctx.Publisher.Add(pblshr1); // Ах, Attach не вправляет сущностям статус на создание, мне тоже нужно помнить, что новым объектам надо делать Add, а старым Attach, иначе всё сломается
                              // А может, статус сущности будет как-нибудь сам распознаваться?
 
dbctx.SaveChanges();


Здесь удивительно:
  1. Надо вручную инициализировать первичные ключи. Не лучше ли иметь какую-то отдельно прописанную закономерность по генерации первичного ключа? Ну, и возможности по управлению;
  2. Для связывания сущностей надо не только установить navigation property, но и проставить правильно внешний ключ, иначе Entity Framework заругается на нарушение ссылочной целостности. Непонятно: если каждая сущность идентифицируется ключом, то связка через публичное поле автоматически должна означать реляционное отношение. Это так легко, просто означить публичное поле, но нееет, давай-ка, дорогой программист, помни, что у нас есть первичные ключи и внешние ключи, означивай сиди, будто делать больше нечего;
  3. Да, есть подход, когда ключи для объектов создаются сами в БД. С одной стороны, почему бы и нет, особенно, если ключ целочисленный, но с другой стороны, пока я не выполню запрос по сохранению, я не имею возможности понять, какой будет ключ, и использовать его дальше. Особенно это забавно обрабатывать, когда сконструированный объект нужен в памяти и с ключом, но, требуется ли его сохранение в БД, определяется в самом конце.
  4. Прицепление или добавление объектов в контекст производится только вызовом метода у коллекции соответствующего типа. Причем используются разные методы: для добавления — Add, а для прицепления существующего — Attach. Что так «удобно»? Может в вопросе что, куда и как добавить, Entity Framework совместно с .Net как-нибудь без меня станут разбираться, какого типа эта сущность и что с ней произошло?
  5. Зачем существует контекст? Чтобы ещё подкинуть мозгам программиста разных «интересных» загадок? Например, подсовывать ему из контекста экземпляры каких-нибудь заглушек, а не самих сущностных классов. Или, например, что будет, если попробовать связать объекты, взятые из разных контекстов? То-то, наверное, будет весело. Значит, надо помнить, что из какого контекста я достал.

Что-то многовато телодвижений для такой простой задачи, как конструирование и связывание сущностей друг с другом.

Как спрашивают СУБД или «недо-include»


Давайте-ка посмотрим, что происходит, когда Entity Framework читает что-либо из БД. Это тоже очень интересно. Как и когда он строит и выполняет запросы.

Поглядите на edmx (кусочек), чтобы немного представить задачку:

Кстати говоря, функция экспорта модели в картинку реализована так, чтобы не только доставить разработчику немало веселых и приятных минут, но и скрасить ему досуг.
Если вы экспортируете относительно большую модель, то разрешающая способность картинки будет примерно такая:

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

И потом, вы заметили, что мощности связей мне пришлось указать, «накарябав» на картинке «от руки», т.к. они не видны? Я сделал это нарочно «пострашнее». Вы думаете, в процедуре экспорта содержится ошибка и мощность просто не попала в диаграмму? Нет, всё проще, ошибок нет, мощность честно экспортирована светло-светло серыми символами на белом фоне (поглядите внимательно под концы связей). Сделано это, вероятно, с целью проверки цветоразрешающей способности мониторов, а также тренировки цветовосприятия разработчиков.

Мы хотим вывести название диска, название издателя и название страны. Есть соблазн решить задачу в лоб. А что, удобно. Берём коллекцию из контекста, проходим по связям. Все данные подкачиваются из базы как-то сами. Да можно и не задумываться об этом, всё работает, цель достигнута, чего ещё надо.
Реализовав нехитрую конструкцию, подглядим, какие при этом запросы выполняет EF.
CDLIBEntities dbctx = new CDLIBEntities();
            
dbctx.Database.Log = (s => { textBox1.AppendText(string.Format("{0}{1}", s, Environment.NewLine)); }); //Так будем подглядывать, какие запросы выполняет EF
 
DVD[] dvds = dbctx.DVD.ToArray(); // Посмотрим, как много запросов будет в таком варианте
//DVD[] dvds = dbctx.DVD.Include("Publisher1").Include(@"Publisher1.Country1").ToList().ToArray(); //В таком варианте будет один запрос с joins, однако нужно ли читать ВСЕ поля из связанных таблиц? На практике это кошмар с ужасом
 
for (int i = 0; i < dvds.Length; i++ )
{ 
    textBox1.AppendText(string.Format("{0} {1} {2}", dvds[i].Name, dvds[i].Publisher1.Name, dvds[i].Publisher1.Country1.Name) );
    textBox1.AppendText(Environment.NewLine);
}
 
textBox1.AppendText("ОК" + Environment.NewLine);


Сначала проверим «в лоб», запускаем: смотрите, Entity Framework выполняет целых 5 запросов, причем каждый ещё и внутри своей коннекции.
Вывод текстбокса:
Opened connection at 17.04.2015 11:01:19 +05:00

SELECT 
    [Extent1].[primaryKey] AS [primaryKey], 
    [Extent1].[Version] AS [Version], 
    [Extent1].[Capacity] AS [Capacity], 
    [Extent1].[Name] AS [Name], 
    [Extent1].[Publisher] AS [Publisher]
    FROM [dbo].[DVD] AS [Extent1]


-- Executing at 17.04.2015 11:01:20 +05:00

-- Completed in 11 ms with result: SqlDataReader



Closed connection at 17.04.2015 11:01:20 +05:00

Opened connection at 17.04.2015 11:01:20 +05:00

SELECT 
    [Extent1].[primaryKey] AS [primaryKey], 
    [Extent1].[Name] AS [Name], 
    [Extent1].[Country] AS [Country]
    FROM [dbo].[Publisher] AS [Extent1]
    WHERE [Extent1].[primaryKey] = @EntityKeyValue1


-- EntityKeyValue1: '5cba87e2-2809-4437-9ff3-5abfe0d21536' (Type = Guid, IsNullable = false)

-- Executing at 17.04.2015 11:01:20 +05:00

-- Completed in 6 ms with result: SqlDataReader



Closed connection at 17.04.2015 11:01:20 +05:00

Opened connection at 17.04.2015 11:01:20 +05:00

SELECT 
    [Extent1].[primaryKey] AS [primaryKey], 
    [Extent1].[Name] AS [Name]
    FROM [dbo].[Country] AS [Extent1]
    WHERE [Extent1].[primaryKey] = @EntityKeyValue1


-- EntityKeyValue1: '888c8fd2-1a12-4ec4-90fa-9742c29cae9e' (Type = Guid, IsNullable = false)

-- Executing at 17.04.2015 11:01:21 +05:00

-- Completed in 2 ms with result: SqlDataReader



Closed connection at 17.04.2015 11:01:21 +05:00

Movie 3 Second Publisher USA
Opened connection at 17.04.2015 11:01:21 +05:00

SELECT 
    [Extent1].[primaryKey] AS [primaryKey], 
    [Extent1].[Name] AS [Name], 
    [Extent1].[Country] AS [Country]
    FROM [dbo].[Publisher] AS [Extent1]
    WHERE [Extent1].[primaryKey] = @EntityKeyValue1


-- EntityKeyValue1: '65e0fb16-15aa-4591-8c8b-286e498d1203' (Type = Guid, IsNullable = false)

-- Executing at 17.04.2015 11:01:21 +05:00

-- Completed in 0 ms with result: SqlDataReader



Closed connection at 17.04.2015 11:01:21 +05:00

Opened connection at 17.04.2015 11:01:21 +05:00

SELECT 
    [Extent1].[primaryKey] AS [primaryKey], 
    [Extent1].[Name] AS [Name]
    FROM [dbo].[Country] AS [Extent1]
    WHERE [Extent1].[primaryKey] = @EntityKeyValue1


-- EntityKeyValue1: '658e951e-b56a-4423-b16a-b1dd2c7c293a' (Type = Guid, IsNullable = false)

-- Executing at 17.04.2015 11:01:21 +05:00

-- Completed in 0 ms with result: SqlDataReader



Closed connection at 17.04.2015 11:01:21 +05:00

Movie 0 First Publisher Greece
Movie 1 Second Publisher USA
Movie 4 First Publisher Greece
Movie 2 First Publisher Greece
ОК


Это от того, что EF работает по принципу «отдай то, за что хватают», имея в виду навигационные свойства. За это удобство разработки приходит жесткая расплата: в больших и сложных системах так делать нельзя, т.к. это приводит к расточительному расходованию ресурсов как СУБД, так и приложения.

Хорошо, что EF имеет кое-какие средства для управления загрузкой. Раскомментируем Include. Хвала мудрости EF! Вот теперь-то JOIN под каждый Navigation и всего один запрос.
Вывод текстбокса:
  Opened connection at 17.04.2015 11:03:44 +05:00

SELECT 
    1 AS [C1], 
    [Extent1].[primaryKey] AS [primaryKey], 
    [Extent1].[Version] AS [Version], 
    [Extent1].[Capacity] AS [Capacity], 
    [Extent1].[Name] AS [Name], 
    [Extent1].[Publisher] AS [Publisher], 
    [Extent2].[primaryKey] AS [primaryKey1], 
    [Extent2].[Name] AS [Name1], 
    [Extent2].[Country] AS [Country], 
    [Extent3].[primaryKey] AS [primaryKey2], 
    [Extent3].[Name] AS [Name2]
    FROM   [dbo].[DVD] AS [Extent1]
    INNER JOIN [dbo].[Publisher] AS [Extent2] ON [Extent1].[Publisher] = [Extent2].[primaryKey]
    INNER JOIN [dbo].[Country] AS [Extent3] ON [Extent2].[Country] = [Extent3].[primaryKey]


-- Executing at 17.04.2015 11:03:45 +05:00

-- Completed in 16 ms with result: SqlDataReader



Closed connection at 17.04.2015 11:03:45 +05:00

Movie 0 First Publisher Greece
Movie 4 First Publisher Greece
Movie 2 First Publisher Greece
Movie 1 Second Publisher USA
ОК


Всё или ничего


НО вот ведь беда: если мы просто пишем Include, читаются ВСЕ свойства как своей, так и связанных сущностей. Кажется, это ключ к пониманию того, куда уходит память и скорость. «Пере-include», так сказать. Вызов Include в лоб чему попало — это страшно, особенно ветвистый на много navigation.

Попробуем сделать лучше. Решение: выбрать множество только нужных полей у сущностей и зачитать только их. Такое множество можно назвать проекцией или представлением, кому как удобно и угодно будет. Хорошо, что Entity Framework имеет такую возможность, правда, кривую.
Чтение в проекции:
CDLIBEntities dbctx = new CDLIBEntities();            
dbctx.Database.Log = (s => { textBox1.AppendText(s); });
 
// 1. Связка анонимных типов (связка потому, что придётся для навигационных свойств тоже сделать анонимные типы)
var anon = dbctx.DVD.Select(x => new { primaryKey = x.primaryKey, Name = x.Name, Publisher1 = new {primaryKey = x.Publisher1.primaryKey, Name = x.Publisher1.Name } }).ToArray();
 
// 2. Объявленные типы (аналогично анонимному, только типы явно объявим, пример опустим, он очевиден)
 
// 3. Взять и сделать наследников от нужных сущностей (хотя бы явно типы писать не надо)
DVD_D[] dvd_derived = dbctx.DVD.Select(x => new DVD_D { primaryKey = x.primaryKey, Name = x.Name, Publisher1 = new Publisher_D { primaryKey = x.Publisher1.primaryKey, Name = x.Publisher1.Name } }).ToArray();


Есть 3 варианта:
  1. Объявляем анонимный тип и выбираем в него только то, что нужно;
  2. Пишем специальный класс только с нужными свойствами и используем его;
  3. Делаем наследников от нужных сущностей и заполняем в них выборку.

Что тут неудобно: Неужели так будем писать под каждую выборку? Это продуктивно?

И потом: цель-то наша не читать, а с сущностями дальше РАБОТАТЬ, т.е. писать логику, которая, в результате приводит к изменению значений полей сущностей. Должно быть, сделать это просто: изменить значение свойства у сущности, а затем вызвать у контекста сохранение, когда надо будет, вот так:
// Реально, очень хочется написать что-то навроде
dvd_derived[0].Name = "Зелёная сосиска";
dbctx.SaveChanges();

Не тут-то было: объект этот не создан из контекста и тип его EFу неизвестен. И как нам теперь сохранить изменения?

Кривизна состоит в том, что совершенно непонятно, как сохранить изменения, не взяв объект из контекста ПОЛНОСТЬЮ, со всеми полями.

Что же делать? Изобретают люди! Например, пишут классы под каждый случай проекции и маппируют как-то затем в сущностные классы (понятные контексту) и обратно. Можно также иметь отдельные наборы сущностных классов с наборами свойств, соответствующим выборкам и настройкой на те же таблицы. Можно, можно как-то извернуться. Должно быть, захватывающее занятие. Но суть этого занятия никак не стыкуется с правильной философией, которая состоит в том, что ORM на то и ORM, что конструкции в языке программирования однозначно представляют предметную сущность (ОБЪЕКТЫ). А не так, что часть там, часть тут. Тут так читаем, тут эдак, тут маппируем, тут разрезаем, тут клеим, тут рыбу заворачиваем. Если я взял какую-то сущность в коде, я должен железобетонно быть уверен, что тут всё, что мне нужно, и больше в коде нет ничего, что отвечает за ту же сущность. Разделяй и властвуй, нечего размазывать функциональность. А иначе, никакой это не ORM, а так — классический набор костылей.

Вместо заключения


Цель использования ORM — сосредоточить программиста на решении предметных задач, не теряя в производительности как программиста, так и разрабатываемого им ПО. Производительность труда вообще должна повышаться. На дворе 21 век, а вопрос доступа к данным так и не закрыт. В таком виде это всё красиво для школьных поделок на 10-20 сущностей и уж никак не годится в сколько-то серьёзной задаче.

Короче говоря, я не понимаю. Ну ладно, для меня Entity Framework — диковинка от унаследованных проектов, сами пользуемся другим продуктом, совсем не массовым и не раскрученным, но как же живёт большинство? Ведь во всём мире работают и радуются жизни люди, как-то пишут и выдают продукты? Ну и вообще спокойно спят себе ночами. Ведь я только взялся, а прямо всё рассыпается в руках. Либо серьёзные, большие проекты пишут на чем-то другом (интересно, на чём), либо там пишут всё вручную, во что вообще верится с трудом. Люди, как вы живёте, я не понимаю…

У меня есть ещё вагончик вопросов про запас. Но лучше, наверное, я в следующей статье расскажу вам, какой ORM кажется удобным мне, «лучшие практики», как сейчас модно выражаться. А?

Комментарии (117)


  1. SVVer
    07.07.2015 19:35
    +5

    CodeFirst… Поэтому мне нужны картинки. Компактные, наглядные, удобные. Причем модель должна быть не на одной картинке, а на нескольких, по-частям, выбранным как-то предметно. Ну, и далее, с картинок что-то как-то должно генерироваться, не буду же я писать однотипный код сотнями раз, да ещё и не делать при этом ошибок.

    А почему для визуализации модели нельзя в случае с CodeFirst использовать диаграммы классов? Они, по-моему, удовлетворяют описанным требованиям?


    1. asanych Автор
      08.07.2015 08:20
      +2

      Ответ очевиден. Потому что это именно «визуализация», а никак не «проектирование». Например, никак не учитывается реляционная специфика. Для примера, я хочу прямо с диаграммы указать название таблицы в базе для хранения сущностей определённого класса. Или указать имя поля. Сейчас я могу это легко делать в том, чем я сейчас пользуюсь. CodeFirst+«визуализация», не могу, мне придётся пойти в код и сделать это «вручную». CodeFirst ведь. :-)
      Опять же, нарисовал, нажал кнопку, — получил классы и БД. Если нужно получить изменения: нажал кнопку, получил DDL для изменения структуры базы и опять же код классов соответственно изменился. А в код лезть в каких-то очень исключительных случаях.


      1. lair
        08.07.2015 11:30

        Ответ очевиден. Потому что это именно «визуализация», а никак не «проектирование».

        А вы не говорили о проектировании изначально, вы говорили именно о визуализации. «Ввод нового человека в проект» — это объяснение существующего кода, а не создание нового.

        Для примера, я хочу прямо с диаграммы указать название таблицы в базе для хранения сущностей определённого класса. Или указать имя поля. [...] Опять же, нарисовал, нажал кнопку, — получил классы и БД. Если нужно получить изменения: нажал кнопку, получил DDL для изменения структуры базы и опять же код классов соответственно изменился. А в код лезть в каких-то очень исключительных случаях.

        Визуальное программирование во всей его красе со всеми его недостатками. Спасибо, не здесь, я этого наелся и больше есть не хочу и не буду.


      1. SVVer
        08.07.2015 11:36
        +1

        Думаю, ответ вовсе не очевиден! Почему CodeFirst так быстро набирает популярность и фактически становится приоритетным подходом при использовании EF?
        Кроме того, одна из задач ORM, избавить разработчика бизнес-логики от нюансов системы хранения и сделать модель предметной области чистой. И в этом смысле работая с диаграммой классов, я именно проектирую такую модель. Естественно, вопрос отображения этой модели в БД тоже важен. Но решается он не визуально на диаграмме, а в коде, и меня лично это совсем не смущает, поскольку те места, где это отображение определяется при использовании fluent api, заведомо известны.
        Но у меня тогда еще вопрос: если Вы используете некую «замечательную» альтернативу EF, которая позволяет делать все гораздо удобнее, то она либо «серебряная пуля» (но Вы сами в одном из комментариев признали, что ее нет), либо что-то, обладающее другими недостатками. Так давайте же тогда озвучим эту альтернативу и будем сравнивать EF, не с потенциальными «пожеланиями» к ORM, а с реальным конкурентом. Иначе, как-то однобоко получается.


        1. asanych Автор
          08.07.2015 11:56
          +2

          В плане проектирования от модели с диаграммы классов я с вами согласен на 100% всеми конечностями, просто инструмент другой у меня. И в плане донастройки в коде, тоже. Позиция моя в том, что в код лезть надо только за очень тонкими настройками.
          К сожалению, назвать инструмент я не могу, я уверен, это сочтут открытой рекламой, что противоречит правилам. Но рассказать про него я обещаю в продолжении. Надеюсь, я смогу его выпустить. Пожалуйста, дождитесь продолжения.


  1. gandjustas
    07.07.2015 19:40
    +10

    В статье яркий пример захода со своим уставом в чужой монастырь.


    1. asanych Автор
      08.07.2015 07:29
      +3

      Смотря что считать «чужим монастырём». Если EF, — то да. Я про это сразу написал в 4-м абзаце.


      1. gandjustas
        08.07.2015 12:13

        Проблема не в монастыре, а в уставе ;)


        1. asanych Автор
          08.07.2015 12:26
          +1

          Гы ;) Спорно.


          1. gandjustas
            08.07.2015 20:28

            Бесспорно. Подплавляющее большинство используют EF совершенно не так как вы описали. Это и вам минус за непонимание и статье минус, за астронавтику.


            1. asanych Автор
              09.07.2015 07:34
              +2

              За непонимание чего? Того, что «большинство используют EF совершенно не так как вы описали»? Я это понимаю, в чем вопрос? Мне было интересно, как используют, я получил ответ, действительно пользуют code-first. Поясните мысль про астронавтику.


              1. gandjustas
                09.07.2015 16:04

                Можно было и заранее погуглить как используется EF, а не разводить флейм.
                Про астронавтику вот — local.joelonsoftware.com/wiki/%D0%9D%D0%B5_%D0%B4%D0%B0%D0%B9%D1%82%D0%B5_%D0%90%D1%81%D1%82%D1%80%D0%BE%D0%BD%D0%B0%D0%B2%D1%82%D0%B0%D0%BC_%D0%90%D1%80%D1%85%D0%B8%D1%82%D0%B5%D0%BA%D1%82%D1%83%D1%80%D1%8B_%D0%B2%D0%B0%D1%81_%D0%B7%D0%B0%D0%BF%D1%83%D0%B3%D0%B0%D1%82%D1%8C

                Помните, что люди архитектуры решают проблемы, которые, как они думают, они могут решить, а не те проблемы, которые полезно решать


  1. muradovm
    07.07.2015 19:43

    Зачем «держать структуру системы в голове» для CodeFirst? Кажется, вы зря отвергли этот вариант.


    1. gandjustas
      07.07.2015 19:45
      +1

      Ну почему же зря, было бы сложнее ругать.


  1. gandjustas
    07.07.2015 19:46
    +2

    Вообще автору можно начинать радоваться, в EF7 model-first выпилят нафиг и больше проблем не будет. А Database-first будет просто генератором модели для codefirst.


    1. aikixd
      07.07.2015 21:30
      -1

      Я вообще предпочитаю Everything first. Полный контроль над базой данных, позволяет убрать из кода практически все возможные правила, а контроль над кодом позволяет внедрить бизнесс логику прямо в проект EF и не оборачивать его бесполезными обертками.


  1. biophreak
    07.07.2015 21:08
    +3

    В целом — статья ни о чем, из серии «не осилил» или «не очень-то и хотелось». Я уверен, что если бы Вам действительно было бы надо, то Вы сами бы дошли до диаграмм классов (как советовали выше).
    А с Include Ваше «негодование» вообще непонятно, в доках же вроде все четко и ясно описано.


    1. asanych Автор
      08.07.2015 08:00
      +5

      Статья о совершенно конкретных проблемах. В руки попадают проекты, у которых, например, code-first и х.з. как настроено отображение в базу. И ни одной картинки. И сотни классов. Разобраться очень трудно.
      Признаю вашу правоту в том, что «не очень-то и хотелось», т.к. в новые проекты я EF не беру, есть другой инструмент, который я считаю более удобным как по концепциям, так и по реализации. Там и диаграммы классов есть и много ещё чего интересного, напишу, если дадут.
      С Include негодование очень даже понятное. Когда я вижу что-то навроде:

      Толстый include
      return Entities
       .Include("Act")
       .Include("Act.Zags")
       .Include("PeopleBorn")
       .Include("PeopleBorn.PeopleInfo")
       .Include("PeopleBorn.PeopleInfo.Passport")
       .Include("PeopleBorn.PeopleInfo.Passport.OrganizationInfo")
       .Include("PeopleBorn.Locations")
       .Include("PeopleBorn.Locations.Location")
       .Include("Father")
       .Include("Mother")
       .Include("Father.PeopleInfo")
       .Include("Father.PeopleInfo.Passport")
       .Include("Father.PeopleInfo.Passport.OrganizationInfo")
       .Include("Mother.PeopleInfo")
       .Include("Mother.PeopleInfo.Passport")
       .Include("Mother.PeopleInfo.Passport.OrganizationInfo")
       .Include("Father.Locations")
       .Include("Mother.Locations")
       .Include("Father.Locations.Location")
       .Include("Mother.Locations.Location")
       .Include("Declarant")
       .Include("Declarant.PeopleInfo")
       .Include("Declarant.PeopleInfo.Passport")
       .Include("Declarant.PeopleInfo.Passport.OrganizationInfo")
       .Include("Declarant.Locations")
       .Include("Declarant.Locations.Location")
      


      1. unicrus
        08.07.2015 08:31

        Там и диаграммы классов есть и много ещё чего интересного, напишу, если дадут.

        Ради этого ведь все и написано? :)

        Получилась статья не «глазами постороннего», а «ищем недостатки». При этом еще и разбираться в предмете «не очень-то и хотелось».


        1. asanych Автор
          08.07.2015 09:06
          +1

          Ради этого ведь все и написано? :)

          Оффтопик.
          Ну, каждый пользуется чем-то своим, может более любимым, чем что-то конкурирующее. Нет ничего безликого. И есть преимущества и недостатки. Это мотивирует на то, чтобы рассказывать и дискутировать.
          В остальном, есть правила.


      1. gandjustas
        08.07.2015 12:35
        -1

        В руки попадают проекты, у которых, например, code-first и х.з. как настроено отображение в базу.

        А чем диаграмма помогает? Тыкать в каждый квадратик и смотреть мапинги? В code-first попроще, там вполне читаемый код в model builder.

        если бы был предложен инструмент, позволяющий это быстро сделать, не было бы ему так лениво написать по-человечески

        Это и есть мапилки, коих полно. Руками выписывать все проекции крайне муторно независимо от того есть ли ORM вообще или нет. Даже на голом SQL опытные DBA частенько забивают писать все проекции.

        Прежде чем писать статью стоило бы инструменты поизучать, а то ты пишешь про «проблемы», которые все пользователи EF давно решили.

        ЗЫ. В code first по умолчанию lazyload не работает.


        1. asanych Автор
          08.07.2015 13:31
          +1

          Диаграмма помогает как минимум сразу оценить систему и проще понимать структуру базы. Причем, диаграмма рисованная человеком. Хоть понятно, о чем люди думали, когда проектировали.

          Я уже соглашаюсь в очередной раз, что инструменты надо знать лучше, предела совершенству нет. Меня просто удивило, что народ решает проблемы, которые и решать-то вроде не надо (я просто не сталкивался).


          1. lair
            08.07.2015 13:35

            Ээээ, а ничего, что диаграмма классов в коде и диаграмма объектов в БД могут друг к другу относиться очень слабо? И не надо одно использовать для понимания другого.


            1. asanych Автор
              08.07.2015 13:54
              +1

              Тут как бы нет однозначного ответа.

              Я считаю так.
              Если система целиком в пределах ваших, вашей конторы или ещё как-то контролируется, либо есть архитектурная группа, которая контролирует разработку через множество подрядчиков прямым демократическим образом, вы выработаете архитектуру и ФОРМАЛЬНЫЕ правила для моделирования и преобразования в код и в базу. И даже можете это автоматизировать. Народ пишет свои фреймворки, свои слои для доступа к данным, чтобы СЕБЕ обеспечить слабосвязанность на будущие изменения. Кто-то даже при этом генераторы кода пишет для повышения производительности, суть, на чём, не важна, T4 там или ещё что.
              Системы, в которых этому не следуют, суть — не имеют архитектуры, а по-русски безобразны. Примеров этому — много, противоположных, — мало.

              Если нужна интеграция с чужой системой именно на уровне СУБД, тут начинаются варианты. Однако, неплохо иметь ORM, который позволяет настроиться на чужую базу тем или иным образом.


              1. lair
                08.07.2015 14:24

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


                1. asanych Автор
                  08.07.2015 14:43
                  +1

                  Если формальные правила есть, и вы их заведомо осознаёте (архитектура), то по модели будет видно.


                  1. lair
                    08.07.2015 14:47

                    Нет, по модели можно будет только предполагать. И только в определенном — не очень широком — спектре случаев. А в реальности — по крайней мере, в моем опыте — между БД и концептуальным слоем очень много различий, причем зачастую ad-hoc (например, если БД — legacy).


                    1. asanych Автор
                      08.07.2015 14:50
                      +1

                      Ну, выходит, у нас разный опыт.
                      Мой опыт показывает, если нет однозначного подхода, нет моделей, — нет контроля, жди сюрприза.


                      1. lair
                        08.07.2015 14:52

                        Почему же, однозначный подход вполне есть. Просто он не «маппинг 1-в-1».


                        1. asanych Автор
                          08.07.2015 15:01
                          +1

                          Ну и я говорю, однозначный подход должен быть. И не всегда это маппинг. Но он должен быть.
                          Мы уже далеко отдалились от темы. :-)


                          1. lair
                            08.07.2015 15:04

                            Тем не менее, этот «однозначный подход» не позволяет, глядя на модель сущностей, определить структуру БД.


                            1. asanych Автор
                              08.07.2015 15:07
                              +1

                              Подход подходу рознь. Сильно зависит от проекта, людей, внешних условий, корпоративных политик, размеров контор и т.п.
                              К великому сожалению, у большинства унаследованных проектов даже при наличии подхода модели сущностей нет и её приходится восстанавливать по базе или по коду.


                              1. lair
                                08.07.2015 15:10

                                Code is your documentation, вообще-то. В диаграмме нет никакой информации, которой не было бы в коде, следовательно, она восстановима из кода. Расположение — это все мелочи, которые влияют на восприятие, но не на суть.

                                (кстати, это как раз фундаментальное достоинство code-first)


                                1. asanych Автор
                                  08.07.2015 15:20
                                  +1

                                  Вот в этом я с вами никогда не соглашусь.
                                  Есть вопрос легкости восприятия.
                                  Идите расскажите заказчику, что он должен вникнуть в ваш код. А и кода ещё и не бывает в моменты обсуждения, до него тупо не дожили.
                                  Я предпочитаю обсуждать с ним картинки. Нет-нет, конечно, я не гружу его структурой сущностей в деталях. Однако use-cases, activity, sequence, statechart, вполне адекватно воспринимаются практически любым заинтересованным неподготовленным человеком. Это легко осваивается. На крайняк, люди любят BPML (правда, я обычно не использую).
                                  Так что проектирование должно идти от картинок, плавно трансформируясь в структуры сущностей, алгоритмов и т.п.
                                  Опять отвлеклись :-)


                                  1. lair
                                    08.07.2015 15:22
                                    +1

                                    Так-так, вот как только вы привлекли сюда заказчика, разговор вообще потерял всякий смысл. EF — это детали реализации, сюда заказчику вообще не надо. А идея, что из модели для презентации заказчику можно сгенерить работающий код, умерла уже годы назад.

                                    Проектирование должно идти от головы. Все остальное — тонкости.


                                    1. asanych Автор
                                      08.07.2015 15:36
                                      +1

                                      Из модели для презентации — конечно нет. :-)
                                      Но вообще-то моделирование с генерацией исходного кода активно применяется.
                                      Причем, для самых разных задач. Помню, лет 15 назад меня сильно поразил один продукт, в котором на UML-based языке (как пример) была запущена стиральная машина. И дебажить было можно прямо по диаграмме.
                                      И сейчас знаю, в т.ч. отечественные конторы, которые делают как рилтайм, так и бизнес-автоматизацию с применением аналогичных подходов, более или менее формальных и проработанных.
                                      Опять отвлеклись :-)


                                      1. lair
                                        08.07.2015 15:37

                                        Из модели для презентации — конечно нет. :-)

                                        Ну вот поэтому все «я не могу показать ваш код заказчику» и не подходят как аргумент.


                                        1. asanych Автор
                                          08.07.2015 15:44
                                          +1

                                          Ладно, считайте что код и есть документация. Сдаюсь. :-)
                                          Изучайте исходники с коллегами. Или фольклорным способом. :-)


          1. gandjustas
            08.07.2015 20:27

            Далеко не все хотят рисовать диаграммы. В EF например начала появился database first с диаграммами, мотом model first, а потом codefirst. Причем именно после появления codefirst люди начали использовать EF, причем codefirst настолько прижился, что остальные методы решили выпилить как ненужные.

            Ваш опыт кардинально противоречит общей практике. Поэтому и статья глуповато выглядит.


            1. asanych Автор
              09.07.2015 07:38
              +1

              Ну, не рисуйте. Разве я настаиваю? Вы используете CF, вам это нравится, мне нет. Я же за это не пытаюсь вам давать мою личную эмоциональную оценку.
              «Общая практика» — вообще вещь странная. Люди же что-то новое изобретают? Оно обычно противоречит общей практике, и что?


              1. gandjustas
                09.07.2015 16:07

                Изобретают много, приживается мало. Причем по объективным причинам. CF прижился, model-first — нет. Есть объективые причины по которым CF получше model-first. Ваша личная оценка не имеет никакого значения.
                В статье очевидна попытка личное мнение выставить как объективное.


                1. asanych Автор
                  09.07.2015 16:33
                  +2

                  Ну, опять двадцать пять.
                  Я не спорю с тем, что прижилось, а что нет, спорить с этим бессмысленно. Как пишут, так пишут. И всё, факт. Нужно с ним считаться, хочется мне или нет.
                  Но при этом никто не мешает дать мне свою оценку, да и послушать, что народ скажет.
                  Вы — субъект и я субъект. Обе наши оценки субъективны. И значения их, думается, одинаковы. А далее, каждый читатель решает для себя.
                  За сим, предлагаю закруглиться и далее не флеймить.


                  1. gandjustas
                    09.07.2015 16:37
                    -2

                    И кому нужна ваша оценка? Тем более в таком тоне.

                    Люди, как вы живёте, я не понимаю…


                    Наши оценки могут быть субъективны, но моя совпадает с сотней тысяч других, а ваша — нет.


  1. leschenko
    07.07.2015 21:14

    Когда счёт идёт на сотни, вы этого уже не можете.

    Помнить назначение всех 100500 таблиц (или моделей) никто не будет, ни при каком подходе. Не получится.

    НО вот ведь беда: если мы просто пишем Include, читаются ВСЕ свойства как своей, так и связанных сущностей.

    Поэтому надо использовать LINQ и доставать только те поля, которые реально нужны.

    PS: Если бездумно что-то использовать, то любой инструмент — плохой.


  1. lair
    07.07.2015 21:33
    +11

    Раз он доступен сразу из коробки с Microsoft Visual Studio…

    С какой Visual Studio доступен EF 6+?

    Кроме очевидных архитектурных и программистских глупостей, [...] были и проблемы, связанные только с Entity Framework.

    Какие именно?

    CodeFirst и DBFirst не предлагать.

    Ну вот и зря. Пользовали бы code-first (который, на самом деле, model-in-code) — не было бы у вас ни одной из проблем, описываемых в вашей борьбе с model-first.

    Мой аргумент прост: пока у вас 5-10-15 сущностей, вы можете держать структуру системы в голове. [...] Когда счёт идёт на сотни, вы этого уже не можете. [...] Поэтому мне нужны картинки. Компактные, наглядные, удобные. Причем модель должна быть не на одной картинке, а на нескольких, по-частям, выбранным как-то предметно.

    Диаграммы классов решают ваши проблемы.

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

    А что за однотипный код вы пишете сотнями раз, и откуда у вас возьмутся корректные картинки для его генерации? Кстати, T4 прекрасно генерит code-first-модели.

    Надо вручную инициализировать первичные ключи.

    Нет, не надо. Просто проставьте их как создаваемые на стороне БД.

    Для связывания сущностей надо не только установить navigation property, но и проставить правильно внешний ключ,

    Тоже не надо, в правильно сконфигуренной модели это делается автоматически. Вот обратно (проставить ключ и автоматически получить заполенное свойство) не работает, и Required не работает ожидаемым образом, но это уже другой разговор.

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

    А, так вам внезапно надо знать, какой ключ будет у объекта до его сохранения? Тогда инициализируйте его сами, он же вам нужен. Да, еще у объектов есть конструкторы.

    Прицепление или добавление объектов в контекст производится только вызовом метода у коллекции соответствующего типа. Причем используются разные методы: для добавления — Add, а для прицепления существующего — Attach. Что так «удобно»?

    Да, так удобно, потому что это операции от совершенно разных действий. Add — это обычное добавление из CRUD, а Attach — это операция из detached entity management. В норме вы используете либо всегда одно, либо всегда другое, но не оба сразу. Да, и именно поэтому после Attach нужно проставлять статусы.

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

    Если вы хотите, чтобы EF за вас определял, какого типа сущность вы добавляете, вас спасет простейший хелпер-метод:
    T Add<T>(this DbContext dbContext, T entity) { return dbContext.Set<T>().Add(entity);}
    


    А что с сущностью произошло, EF и так определяет за вас, до тех пор, пока вы не нарушаете трекинг.

    Зачем существует контекст?

    Чтобы поддерживать unit of work.

    Или, например, что будет, если попробовать связать объекты, взятые из разных контекстов? То-то, наверное, будет весело.

    Если это tracked entities, то будет ошибка при сохранении и/или аттаче. Если это non-tracked entities — ничего не будет, будет работать.

    Что-то многовато телодвижений для такой простой задачи, как конструирование и связывание сущностей друг с другом.

    Srsly?

    dbContext.Set<Books>().Add(new Book{Title = "EF", AuthorId = 15, Tags = new []{new Tag("programming"), new Tag("tutorial")}});
    dbContext.SaveChanges();
    


    Что тут неудобно: Неужели так будем писать под каждую выборку? Это продуктивно?

    Да, это продуктивно. Особенно это продуктивно в сочетании с соответствующими инструментами навроде мапперов и контролов.

    И потом: цель-то наша не читать, а с сущностями дальше РАБОТАТЬ, т.е. писать логику, которая, в результате приводит к изменению значений полей сущностей.

    А вот это совершенно не обязательно. Точнее, это верно только в том случае, если у вас stateful application (толстый клиент, проще говоря, или сервис какой). А в тонких клиентах операции прекрасно разделяются на те, где нужно прочитать много, и те, где нужно обновить что-то.

    Нет, понятно, что EF — не панацея. Но это удобный ORM с LINQ-интерфейсом, активно растущий и потихоньку избавляющийся от своих болячек. Основная-то масса описанных проблем все равно проистекает из самой концепции ORM (aka OR impedance mismatch), а не из реализации в EF.


    1. gandjustas
      07.07.2015 21:38
      -2

      Реквестирую статью по мотивам коммента, а то уже надоели статьи неосиливших.


      1. lair
        07.07.2015 21:43
        +2

        Да есть же прекрасная серия книжек Лерман про EF/Code-first/DbContext.


        1. chumakov-ilya
          08.07.2015 20:44

          Соглашусь, что статья все равно нужна. По этой теме на Хабре полно материалов с кучей негативных отзывов в комментариях, а вот убедительных примеров хорошей архитектуры не найти.


      1. mird
        07.07.2015 21:49
        +1

        А смысл? Есть пара книжек, которые это прекрасно описывают: Programming Entity Framework Code First и Programming Entity Framework DbContext. А в статью все не уместишь.


        1. chumakov-ilya
          08.07.2015 20:45
          +1

          А всё и не надо, достаточно одного законченного примера красивого архитектурного решения из реальной жизни со списком сработавших граблей.


          1. asanych Автор
            09.07.2015 07:42
            +3

            Не могу не согласиться, ознакомлюсь с удовольствием. Вопросы при приготовлении возникают. Много людей пишет на EF.


    1. asanych Автор
      08.07.2015 08:57

      Спасибо за комменты, они очень подробны.

      С какой Visual Studio доступен EF 6+?

      Вы прекрасно понимаете, о чём я говорю, EF-технология ORM «по-умолчанию». Всем же ясно, что это и кем сделано. Разумеется, чисто технически вам надо поставить соответствующий пакет. Пойду так и исправлю статью.
      Диаграммы классов решают ваши проблемы.

      Нет, в том виде, как реализовано в студии, не решают, я ответил товарищу выше, почему.
      А что за однотипный код вы пишете сотнями раз, и откуда у вас возьмутся корректные картинки для его генерации?

      Я считаю, что код по описанию в классах сущностей, их свойств и т.п. является однотипным, разве нет? Это сущности и их отношения и ничего более.

      Надо вручную инициализировать первичные ключи.

      Нет, не надо. Просто проставьте их как создаваемые на стороне БД.
      А, так вам внезапно надо знать, какой ключ будет у объекта до его сохранения? Тогда инициализируйте его сами, он же вам нужен. Да, еще у объектов есть конструкторы.

      Представьте себе, да, надо знать заранее. Есть масса задач, где это надо. Например, есть устройство самообслуживания, не имеющее постоянного доступа к БД. И ключи сущностей знать надо заранее, чтобы хотя бы логи потом разбирать, чтобы они были одинаковые, как в БД, так и на устройстве.
      Вы предлагаете мне написать в каждый конструктор означивание первичного ключа. Или в каждом месте, где это потребуется. Сейчас у меня другой способ: в ORM, которую я юзаю, есть понятия «тип первичного ключа» и «генератор первичного ключа». И делаю я это декларативно, один единственный раз. Если мне нужно какой-то особенный закон генерации ключей (да, есть задачи, где недостаточно обычного GUID или целого), я пишу свой генератор и также один раз декларативно подключаю.
      Да, это продуктивно. Особенно это продуктивно в сочетании с соответствующими инструментами навроде мапперов и контролов.

      Представляете, я не пишу никаких мапперов! Я просто декларативно определяю проекцию как множество собственных полей и полей связанных сущностей. И дальше её использую. Более того, такие множества специально именуются, так, что я всякий раз, когда мне надо, просто беру проекцию по имени, и всё. «Дай сущности в такой-то проекции». Всё. Более того, проекция задаётся прямо на диаграмме классов, визуально, в диалоговом режиме. Поверьте, делается это очень, очень быстро.
      Да, так удобно, потому что это операции от совершенно разных действий. Add — это обычное добавление из CRUD, а Attach — это операция из detached entity management. В норме вы используете либо всегда одно, либо всегда другое, но не оба сразу. Да, и именно поэтому после Attach нужно проставлять статусы.

      Представляете, когда я сейчас пишу прикладной код, я не думаю о CRUD, состояниях объектов, и какие там действия предпримет ORM (хотя могу). ORM делает это сам, как и резолвит зависимости между связанными сущностями. Ведь ежу понятно, что если я сделал new, будет только insert в БД, а если я что-то прочитал, то create всяко не будет.
      Нет, понятно, что EF — не панацея. Но это удобный ORM с LINQ-интерфейсом, активно растущий и потихоньку избавляющийся от своих болячек. Основная-то масса описанных проблем все равно проистекает из самой концепции ORM (aka OR impedance mismatch), а не из реализации в EF.

      Позащищаю я ORM. Как обычно, конечно же нет никаких «серебряных пуль» и всякая задача прежде всего инженерная задача. А всякий инструмент обладает своими ограничениями. И инструмент подбирается внимательно и тщательно. Сама концепция ORM — очень хороша, т.к. повышает производительность труда. И это хорошо. Просто, похоже EF — не очень хорошая реализация, и это удивительно для меня, человека, который просто столкнулся с ней сильно после всех остальных (так уж случилось) и глянул как на что-то новое. ORM должен позволять настроить всё очень тонко и очень детально. Ну и конечно, там где ORM явно плох, не надо его использовать. Например, писать отчёты. Ну, вы поняли…
      Если будет возможность, напишу отдельную статейку, как планировал, по поводу того, что мне хочется от ORM.


      1. leschenko
        08.07.2015 11:16

        Надо вручную инициализировать первичные ключи.

        Представьте себе, да, надо знать заранее.

        Так Вы разберитесь, Вы за красных или за белых?

        ORM должен позволять настроить всё очень тонко и очень детально.

        CodeFirst позволяет настроить все очень тонко и очень детально. Но Вы в упор отказываетесь это принять. Только в этом Ваша проблема.


        1. asanych Автор
          08.07.2015 11:32
          +1

          Так Вы разберитесь, Вы за красных или за белых?

          Я ни за тех, ни за других. Когда-то так, когда-то эдак. Бывает много факторов, самых неожиданных. Я вам привёл пример, когда идентификатор сущности надо знать сразу, иначе потом концов не собрать.
          CodeFirst позволяет настроить все очень тонко и очень детально. Но Вы в упор отказываетесь это принять. Только в этом Ваша проблема.

          Ок. Приведите мне пример, как настроить следующее: вот мне надо, чтобы когда идёт update запрос на обновление числового поля в БД, ORM не просто тупо подставил значение как константу (x = значение), а выражением, как разницу между значением, которое было при зачитывании и при обновлении (как x = x + РазницаСтарогоИНовогоЗначений). Я надеюсь, вы понимаете, зачем такое может быть нужно.


          1. lair
            08.07.2015 11:46

            Приведите мне пример, как настроить следующее: вот мне надо, чтобы когда идёт update запрос на обновление числового поля в БД, ORM не просто тупо подставил значение как константу (x = значение), а выражением, как разницу между значением, которое было при зачитывании и при обновлении (как x = x + РазницаСтарогоИНовогоЗначений).

            Я не понимаю, зачем такое может быть нужно (особенно на уровне ORM). Опишите задачу.


            1. asanych Автор
              08.07.2015 12:07
              +1

              Что угодно считать. Например, деньги.
              Как решать следующую задачу:
              Есть сущность Счет.
              Есть объект в базе, у которого Счет.Сумма=100.
              1. Два процесса подняли этот объект одновременно (ну, почти одновременно).
              2. Затем первый сказал что Счет.Сумма нужно увеличить на 100.
              3. Второй сказал что Счет.Сумма нужно увеличить ещё 100.
              4. В базе Счет.Сумма = 300 (должно быть).
              Как вы бы решали эту задачу?
              Если пишутся константные значения, то будет бага, ибо запишется 200, т.к. второй перепишет значение первого.


              1. lair
                08.07.2015 12:13

                Есть объект в базе, у которого Счет.Сумма=100.
                1. Два процесса подняли этот объект одновременно (ну, почти одновременно).
                2. Затем первый сказал что Счет.Сумма нужно увеличить на 100.
                3. Второй сказал что Счет.Сумма нужно увеличить ещё 100.
                4. В базе Счет.Сумма = 300 (должно быть).

                Это заведомо неправильная задача, причем она неправильная на уровне бизнеса. Как вы отличаете «пользователь сказал, что сумму счета надо увеличить на сто» от «пользователь сказал, что сумму счета надо сделать равной двести»?

                В EF изначально заложена оптимистическая конкурентность, и он (при настройках по умолчанию) просто не даст сделать вторую операцию, увидев, что данные, к которым применяется update, изменились. Я не знаю способа сделать в нем второе поведение, но, как говорилось выше, я считаю модель, приводящую к такому поведению, ошибочной. Если бы мне надо было реализовывать подобные требования, скорее всего, они бы свелись к другой модели (явных транзакций/операций по счету).


                1. asanych Автор
                  08.07.2015 12:22

                  Я не понял, кто увидит, откуда увидит.
                  Процессы исполняются на физически разных машинах.


                  1. lair
                    08.07.2015 12:29

                    У EF есть как минимум два механизма проверки конкуренции (оба на уровне БД).

                    Первый:

                    UPDATE Accounts
                    SET Amount = 200
                    WHERE Id = @id AND Amount = @prevAmount
                    


                    Второй:
                    UPDATE Accounts
                    SET Amount = 200, RowVersion = @newVersion
                    WHERE Id = @id AND RowVersion = @prevVersion
                    


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


                    1. asanych Автор
                      08.07.2015 12:39

                      Но ведь вы согласитесь со мной, что такой способ создаёт бОльшую нагрузку на СУБД, нежели просто использовать относительные значения?
                      И потом, мне ошибка контроля конкуренции не нужна. Мне молча нужно получить 300 в этом поле этой записи в БД и всё.


                      1. lair
                        08.07.2015 12:42

                        Но ведь вы согласитесь со мной, что такой способ создаёт бОльшую нагрузку на СУБД, нежели просто использовать относительные значения?

                        Во-первых, нет, не соглашусь.

                        Мне молча нужно получить 300 в этом поле этой записи в БД и всё.

                        Во-вторых, как я писал выше, это ошибочное поведение с точки зрения бизнеса. Я повторю свой вопрос: как именно вы отличаете ситуацию «пользователь ввел 200 потому что было 100 и он хочет на 100 больше» от ситуации «пользователь ввел 200 потому что хочет 200»?

                        Стандартное решение для нужного вам поведения (вне зависимости от используемого ORM и вообще технологий) — это хранение транзакций, а не состояния.


                        1. asanych Автор
                          08.07.2015 12:44
                          -1

                          Пример:
                          Идут платежи 300 шт. в секунду.
                          На каждый в БД создаётся запись о «транзакции», т.е. изменение суммы.
                          Текущее состояние счёта как станете пересчитывать, чтобы показать пользователю?
                          Говорить: ай, concurrency conflict?


                          1. lair
                            08.07.2015 12:51

                            Это же классическая задача на CQRS. Для начала я буду пересчитывать текущее состояние счета на уровне БД (т.е., каждая операция по счету будет — атомарно — менять остаток на нем). Если станет понятно, что это не работает под существующей нагрузкой, то пересчет будет делаться не на каждый платеж, а по таймеру с той частотой, с которой БД это комфортно. Если эта частота при этом некомфортна для пользователя, то будет введен промежуточный слой в памяти, где, опять-таки, будет атомарное обновление остатка при каждой операции по счету + регулярная синхронизация из БД.

                            Но вообще, конечно, такие вещи существенно проще делать на реактивных системах (ES, акторы, все вот эти волшебные слова).


                            1. asanych Автор
                              08.07.2015 13:00

                              Ясно. Удачи.
                              Особенно, когда вам придётся масштабировать сервер приложений на несколько физических машин. Причём убавлять-добавлять при необходимости.
                              А я попользуюсь более простым средством: ORM просто вычтет или прибавит, если я скажу, что это значение надо учитывать как относительное.
                              У меня не возникнет проблем ни с изучением, настройкой и т.п. реактивных фреймворков. Также, у меня не возникает привязки к СУБД (ANSI-стандарт) и, если что, я перееду на другую, не изменяя прикладного кода.

                              И потом, настройку реактивных фреймворков никак не сочтёшь тонкой настройкой codefirst, уж как ни крути.


                              1. lair
                                08.07.2015 13:05

                                А я попользуюсь более простым средством: ORM просто вычтет или прибавит, если я скажу, что это значение надо учитывать как относительное.

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

                                Дело — в вашей задаче — не в ORM. Дело в общей архитектуре.

                                (и да, та же akka прекрасно масштабируется на произвольное количество машин, причем еще и прозрачно для кода)


                                1. asanych Автор
                                  08.07.2015 13:08
                                  +1

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

                                  Если значение всегда меняется на относительную величину, должна ошибиться СУБД.
                                  Я такого чуда нигде никогда не видел, сколько живу (разве что в древних-древних версиях MySQL).


                                  1. lair
                                    08.07.2015 13:10

                                    Чтобы получить относительную величину, нужно знать, относительно чего ее вычислить. Как предлагаемая вами система получает базовую величину для отсчета?

                                    (лучше бы, конечно, сразу с примерами кода)


                                    1. asanych Автор
                                      08.07.2015 13:15
                                      +1

                                      Просто разница между значениями, при зачитке и при сохранении.


                                      1. lair
                                        08.07.2015 13:16

                                        Когда происходит «зачитка»? Я ведь не зря код попросил показать.

                                        (собственно, для системы, оптимизируемой под производительность, сам факт двух обращений в БД странен)


                                        1. asanych Автор
                                          08.07.2015 13:43
                                          +1

                                          Вы правы, можно не обращаться первый раз при расчётах, всё равно изменение значения относительное.


                                          1. lair
                                            08.07.2015 14:23

                                            Тогда как же предлагаемая вами система получит базовую величину?


                          1. gandjustas
                            08.07.2015 20:30

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


                            1. asanych Автор
                              09.07.2015 07:45
                              +1

                              Зависит от системы. У меня система (долго погружаться в предметную область), в которой остатки необходимо считать в реальном времени, ну или очень близко к реальному. Например: перебор некоторой суммы означает реальные материальные потери для пользователя.


                              1. gandjustas
                                09.07.2015 16:10

                                Материализованные представления как раз считают в реальном времени. При этом в базу пишутся проводки.
                                Любая другая схема имеет потенциальные проблемы согласованности.


              1. gandjustas
                08.07.2015 12:16

                В EF встроена оптимистичная конкуренция. В вашем случае упадет тот, кто последний вызовет SaveChanges.
                Можно настроить процесс на ваши токены.


                1. asanych Автор
                  08.07.2015 12:24
                  +1

                  Я к тому, что не хочу разбираться на уровне приложения с конкуренцией. С т.з. повышения конкуренции, лучше вообще отдать этот вопрос СУБД, о чём я и толкую.


                  1. lair
                    08.07.2015 12:30

                    Я к тому, что не хочу разбираться на уровне приложения с конкуренцией. С т.з. повышения конкуренции, лучше вообще отдать этот вопрос СУБД.

                    В таком случае у вас бизнес-логика (потому что принятие решений о разрешении конфликтов — это бизнес-задача) оказывается в БД.


                    1. asanych Автор
                      08.07.2015 12:40
                      +1

                      Это не конфликт. Это просто математика. Счёт должен увеличиться и всё. :-)


                  1. gandjustas
                    08.07.2015 12:38

                    Ну так отдайте. Поставьте только расширение небольшое.

                    nuget.org/packages/EntityFramework.Extended
                    github.com/loresoft/EntityFramework.Extended


                    1. asanych Автор
                      08.07.2015 12:43
                      +1

                      Да, видел. Классно, для тех кого EF в остальном устраивает.


                      1. gandjustas
                        08.07.2015 20:32
                        -2

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

                        Остается вопрос, зачем статью написали?


                        1. asanych Автор
                          09.07.2015 07:48
                          +2

                          Я также понял, что EF вам нравится.
                          Также, вы в который уже раз пытаетесь перевести разговор из области фактов и инженерных подходов в область моей личности.


                          1. gandjustas
                            09.07.2015 16:10
                            -1

                            Потому что в статье личное мнение, не имеющее ничего общего со сложившейся практикой.


              1. VladVR
                08.07.2015 19:12

                Такую задачу, решают транзакции.


                1. gandjustas
                  08.07.2015 20:35

                  Такую задачу транзакции не решают.

                  Вот тут подробнее stackoverflow.com/questions/1483725/select-for-update-with-sql-server

                  Решает как раз одиночный update


                  1. VladVR
                    08.07.2015 23:06

                    Одиночный апдейт решает очень ограниченную задачу «прибавить N», но задача внезапно может вырасти до «прибавить N, если ...». Да и вообще бизнес-логика имеет свойство меняться и разрастаться.

                    И, кроме того, как раз таки транзакции решают. У чела(по этой ссылке на stackoverflow) проблема была в том, что у него в where field = val было поле не являющимся уникальным индексом. Если выборку делать по первичному ключу, то работает.
                    Написал сейчас интеграционный тест на это — все прекрасно работает, ровно также как и в оракле select for update.

                    Другая проблема тут в том, что приходится операцию делать в три раунд-трипа до сервера, первый лочит, второй читает текущее значение, третий записывает новое. Первый шаг приходится делать отдельно, поскольку EF (в отличие от например nHibernate) не умеет навешивать updlock на запрос, и приходится исполнять ручной SQL запрос. Хотя и так все равно можно извратиться и объединить первый шаг со вторым, но это уже танцы с бубном и особенно печально, если сценария больше одного.
                    Но если речь о деньгах, то как мне кажется целостность операции более приоритетна чем быстродействие.

                    Хотя с другой стороны странно, что такой, как мне кажется, достаточно востребованный сценарий(лочить строчку) не поддерживается в EF.


                    1. gandjustas
                      09.07.2015 03:06

                      Одиночный апдейт решает очень ограниченную задачу «прибавить N», но задача внезапно может вырасти до «прибавить N, если ...». Да и вообще бизнес-логика имеет свойство меняться и разрастаться.


                      И? Проблема написать where?

                      У чела(по этой ссылке на stackoverflow) проблема была в том, что у него в where field = val было поле не являющимся уникальным индексом.

                      В текущей задаче сумма счета не является уникальным индексом.

                      Но если речь о деньгах, то как мне кажется целостность операции более приоритетна чем быстродействие.

                      Если речь идет о деньгах или об учете вообще, то надо использовать двойную запись. Никаких исключений.

                      Хотя с другой стороны странно, что такой, как мне кажется, достаточно востребованный сценарий(лочить строчку) не поддерживается в EF.

                      Какой именно? Массовый апдейт? Есть в расширении.

                      А вообще массовые операции в прикладной логике — довольно редкий сценарий, чаще всего следствие или ошибки проектирования, или намеренной денормалзации (которая тоже по сути ошибка). Просто потому, что людям не свойственно в программах работать со множествами, в программах работают с конкретными объектами.


                      1. VladVR
                        09.07.2015 09:28

                        И? Проблема написать where?
                        а также пару джоинов, и вообще перенести всю бизнес-логику на DAL слой. Да, это проблема. У автора впрочем тоже эта проблема есть. Его беспокоит необходимость написать 300 мапперов, а то что все части программы срослись в одно целое и их нельзя будет модифицировать не поломав что-то другое это норм.
                        В текущей задаче сумма счета не является уникальным индексом.
                        в текущей задаче ничего не сказано о том, как именно селектится строчка, в которую надо записать x=x+n. Сомнительное удовольствие искать строчку по значению денег, а не по первичному ключу.
                        Какой именно? Массовый апдейт? Есть в расширении.
                        я в скобках написал, какой именно. Лочить строчку, и вообще накладывать на запрос атрибуты подобные WITH (updlock). Когда операции исполняются на разных хостах, синхронизироваться на базе приходится, у нас в проекте как раз и использовался для этого оракловский select for update в транзакции.


                        1. gandjustas
                          09.07.2015 16:14

                          При чем тут слои? nuget.org/packages/EntityFramework.Extended и делайте апдейт из кода, не надо все в базу засовывать.


          1. leschenko
            08.07.2015 12:18

            (как x = x + РазницаСтарогоИНовогоЗначений)

            Да, так сделать нельзя. Но это не относится к «настройке». И это проблема не только EF. Многие ORM поступают так же. Это их не красит, но сам факт.

            Опять проблема: вы используете ORM, но думаете как DB. Т.е. сам факт того, что вы сначала придумали SQL запрос (update… set x=x+1), а потом пытаетесь придумать такой код, который получит этот запрос — плохо.


            1. asanych Автор
              08.07.2015 12:36
              +1

              Я запрос не придумывал, я просто иллюстрирую проблему concurrency, которая, при нагруженных системах с множеством транзакций не решается толком никак по-другому, ибо управление конкуренцией на уровней приложения сильно ограничивает архитектуру и возможности масштабирования, соответственно усложняет логику (это всё чревато глюками).
              Меня упрекнули в том, что всё можно настроить. Я попросил пример, когда нужно вычислительную логику передвинуть на уровень СУБД, если это необходимо. Ну и вообще что угодно вытворить, если очень приспичит.
              Иногда думать как БД — приходится. Её же никуда не денешь.
              Другое дело, что прикладной код нужно стараться максимально изолировать от этой специфики.


              1. lair
                08.07.2015 12:38

                Чтобы вытворить вообще что угодно в EF есть прямой доступ к нижележащему подключению к БД.


                1. asanych Автор
                  08.07.2015 13:05
                  +1

                  Дык, он как бы, у всех есть, я думаю.


                  1. lair
                    08.07.2015 13:06

                    Вот и ответ на ваш вопрос «как сделать что угодно».


              1. leschenko
                08.07.2015 13:06

                Я сталкивался только с одним ORM'ом который позволял генерировать update и delete запросы с выражениями — subsonicproject.com. Но это было в версии 2.x. В версии 3.x опять та же проблема.

                Поэтому поведайте миру (никто не будет считать это рекламой) — какую пулю вы нашли?


                1. asanych Автор
                  08.07.2015 13:11
                  +2

                  Я расскажу, потерпите.
                  Вещь очень хорошая, как и всё, тоже не лишенная недостатков и разных приколов, которые объясняются как спецификой восприятия реальности создателями, так и разными историческими причинами (очень много прикладного кода). Потерпите, пожалуйста, немного.


      1. lair
        08.07.2015 11:28

        Вы прекрасно понимаете, о чём я говорю, EF-технология ORM «по-умолчанию».

        Нет, не понимаю. Нет никакого «по умолчанию».

        Разумеется, чисто технически вам надо поставить соответствующий пакет.

        И именно поэтому установка EF ничем не проще установки NHibernate, Dapper или linq2db.

        Я считаю, что код по описанию в классах сущностей, их свойств и т.п. является однотипным, разве нет?

        Конечно, нет. У каждой сущности — свои свойства (и отношения), и вам все равно надо их где-то описать. Вот реализация INotifyPropertyChanged — однотипная.

        Представьте себе, да, надо знать заранее. Есть масса задач, где это надо. Например, есть устройство самообслуживания, не
        имеющее постоянного доступа к БД. И ключи сущностей знать надо заранее, чтобы хотя бы логи потом разбирать, чтобы они были одинаковые, как в БД, так и на устройстве.
        Вы предлагаете мне написать в каждый конструктор означивание первичного ключа. Или в каждом месте, где это потребуется. Сейчас у меня другой способ: в ORM, которую я юзаю, есть понятия «тип первичного ключа» и «генератор первичного ключа». И делаю я это декларативно, один единственный раз. Если мне нужно какой-то особенный закон генерации ключей (да, есть задачи, где недостаточно обычного GUID или целого), я пишу свой генератор и также один раз декларативно подключаю.

        Если вкратце, то не верю. И не верю я по одной простой причине: вы не можете так сделать, сохранив простой способ работы с сущностями. Код создания первичного ключа где-то должен быть. Если он не в сущности, то вам нужно его активировать, для этого вы должны создать сущность не через new, что, в общем-то, нарушает простоту использования (и, кстати, прекрасно реализуется на EF). Если он в сущности, то это значит, что у вас не POCO-сущности, что нарушает чистоту модели (и, впрочем, все равно реализуется при работе с EF).

        Представляете, я не пишу никаких мапперов! Я просто декларативно определяю проекцию как множество собственных полей и полей связанных сущностей. И дальше её использую. Более того, такие множества специально именуются, так, что я всякий раз, когда мне надо, просто беру проекцию по имени, и всё. «Дай сущности в такой-то проекции». Всё. Более того, проекция задаётся прямо на диаграмме классов, визуально, в диалоговом режиме. Поверьте, делается это очень, очень быстро.

        Снова не поверю. Во-первых, все, что касается «визуально в диалоговом режиме» — это медленно, катастрофически медленно, подверженно ошибкам и не подлежит автоматизации сторонними средствами. Во-вторых, если выкинуть визуальный редактор, то в EF (не из коробки, конечно, надо немного рук применить) это делается приблизительно так: query.Project().To<YourProjectionName>().

        Представляете, когда я сейчас пишу прикладной код, я не думаю о CRUD, состояниях объектов, и какие там действия предпримет ORM (хотя могу). ORM делает это сам, как и резолвит зависимости между связанными сущностями. Ведь ежу понятно, что если я сделал new, будет только insert в БД, а если я что-то прочитал, то create всяко не будет.

        EF работает точно так же.

        Сама концепция ORM — очень хороша, т.к. повышает производительность труда.

        Может повышать. И обязательно ценой чего-то. Просто вам не нравятся те компромисы, которые выбраны в EF.

        ORM должен позволять настроить всё очень тонко и очень детально.

        Нет, ORM — удивительным образом — никому ничего не должен. Чем детальнее вы даете что-то «настраивать», тем сложнее с этим работать.


    1. mayorovp
      08.07.2015 14:44

      Вот обратно (проставить ключ и автоматически получить заполенное свойство) не работает
      Если очень надо — нет проблем такое реализовать. Хотя код, конечно, чуть распухнет…


  1. mayorovp
    08.07.2015 14:43

    Наезд на CodeFirst не понятен. EdmxWriter.WriteEdmx — и ваш CodeFirst контекст превращается в красивую диаграмму. Конечно, там и «связи как попало», и сущности не в том порядке — так что ручная доводка определенно потребуется — но это способ получить красивую картинку есть.

    По поводу длины строк — в CodeFirst специально для вас сделан MaxLengthAttribute. И это не сказки, он и правда в итоге задает ширину строки в БД.

    По поводу ключей — EF может работать с любыми ключами. Хоть генерируемыми лично вами, хоть генерируемыми БД — хоть смешанного типа.

    По поводу прицепления и добавления: context.Entry(...).State = EntityState.ВашеСостояние. Единый способ прицепить сущность к контексту, добавить ее, и даже удалить. Да, можно удалить сущность не прицепляя. Главное — первичный ключ заполнить корректно.


    1. asanych Автор
      08.07.2015 14:47

      Смотрите шире, речь идет не о том, чтобы «получить картинку». А о проектировании структуры сущностей системы.


      1. mayorovp
        08.07.2015 15:46
        +1

        На «выходе» процесса проектирования в принципе не может получиться ничего, что можно автоматически перевести в код. Потому что в противном случае проектировщик тонет во всяких формальных ограничениях, наложенных на инструмент для обеспечения возможности этой самой генерации кода, а также в деталях реализации, несущественных на этапе проектирования. К примеру, те же длины строк — вещь, конечно, нужная — но зачем проставлять их еще когда неизвестно даже общее число таблиц в БД?

        Проектировать надо в инструментах для проектирования — а не в студии. Когда же проект БД готов — ничего не мешает сделать по нему CodeFirst модель. Красивая картинка же — это просто способ окинуть взглядом результат, чтобы проверить его на ошибки. Или способ «врубиться» в чужой проект.

        И, если уж на то пошло, ваш аргумент против CF был такой:

        Мой аргумент прост: пока у вас 5-10-15 сущностей, вы можете держать структуру системы в голове. Пользуйтесь на здоровье! Когда счёт идёт на сотни, вы этого уже не можете.
        Так вот: сгенерированная по коду диаграмма, или даже несколько диаграмм — это отличный способ не запутаться в этих сущностях. Я понимаю, почему вам не подошли диаграммы классов — но почему вам не нравится нормальная диаграмма концептуальной модели БД?


        1. asanych Автор
          08.07.2015 15:53
          +1

          А я, собственно, не предлагаю заставлять проектировщика или аналитика прорабатывать систему в деталях, как и пользоваться для этого студией. «Проектировать надо в инструментах для проектирования — а не в студии» — сильно сказано, я подписываюсь под этим тоже. Хорошо также, когда инструмент проектирования позволяет довести модель до программиста. Вот что я хотел сказать.
          Жаль, что я не могу показать «сгенерированной по коду диаграммы», ни одной. Но очень бы хотелось. :-)


          1. mayorovp
            08.07.2015 16:18
            +1

            Тогда я не пойму, что же вам настолько не нравится в CF, что вы расписали аж целый список из 6 пунктов «недостатков EF», которые уже все решены в CF.


            1. asanych Автор
              08.07.2015 16:26
              +1

              Сам принцип CF (как подхода к проектированию) мне не нравится. И мне кажется, что непопулярность MF (как подхода к проектированию, я подчёркиваю) объясняется качеством реализации в EF.


              1. lair
                08.07.2015 16:34

                Смех ситуации в том, что code-first — это не подход к проектированию. Это всего лишь один из способов разработки с использованием EF.


                1. asanych Автор
                  08.07.2015 16:37
                  +1

                  Да, поэтому люди меня не сразу понимают. Первоначальные значения слов забыты.


        1. asanych Автор
          09.07.2015 08:34
          +1

          Слушайте, я нашёл для вас диаграмму! Автоматически сгенеренную по коду. Всё равно она экспортировалась в таком виде, что разобрать детали невозможно.
          Это структура сущностей средненькой системы, по которым мы консультируем.
          Я думаю, всё-таки вы согласитесь со мной, что запутаться в ней довольно легко. Особенно, в случаях с недостаточным знанием предметной области

          Картинка ~300КБ


          1. mayorovp
            09.07.2015 14:53

            Ну, если бы передо мной стояла задача разобраться в чужом проекте при минимуме документации — я бы примерное с такой диаграммы и начал.

            Если растащить «большие» сущности по разным углам, а остальные поставить вокруг них — картинка уже станет приличнее.

            Но как по вашему в принципе может выглядеть диаграмма для такой БД? Я уверен, когда вы такие БД проектируете — вы разбиваете в своем любимом инструменте одну диаграмму на несколько (десятков). С edmx-файлом тоже можно так сделать: копируем его в нескольких экземплярах — а потом в каждом из них попросту стираем лишнее. А можно сразу генерировать несколько диаграмм — это тоже несложно, если есть какие-то формальные критерии для разделения.

            Кстати, по поводу любимых инструментов. Лично я хороших инструментов, пригодных для работы с диаграммами БД не видел — но если они есть — то перегнать диаграмму из одного формата в другой не так и сложно.


            1. chumakov-ilya
              09.07.2015 15:47

              Зачем вообще генерировать EDMX для того, чтобы разобраться в БД, если можно сгенерировать полноценную диаграмму в SSMS или ей подобных IDE?


              1. mayorovp
                09.07.2015 16:27
                +1

                Разобраться-то хочется именно в сущностях EF, с ними же потом в коде работать — SSMS же «работает» уровнем ниже.


      1. gandjustas
        08.07.2015 20:38

        Code-first называется так именно потому что проектирование делается путем создания классов.

        То есть вы делаете типы, делаете контекст, который автоматом генерит базу на основе соглашений. Потом пишите прикладную логику, а лишь затем настраиваете маппинг классов в базу (если это необходимо).

        Проектирование путем рисования диаграммок устарело лет 10 назад уже.


        1. asanych Автор
          09.07.2015 08:01
          +3

          Code-first и значит, «код в первую очередь», значит идём писать код. Есть там контекст, нет контекста, как и когда пишется логика и маппинг — зависит от реализации.
          Хорошо. Графические нотации (по вашему мнению) устарели. У вас есть все шансы рассказать о современных подходах.


          1. gandjustas
            09.07.2015 16:16
            -2

            Современный подход — писать код, вместо диаграмм. Типы прекрасно создают структуру, лучше чем любая диаграмма и нет проблемы синхронизации кода и диаграммы.


  1. Diaver
    08.07.2015 21:03

    Какая прелесть, «Я не понял что это и не хотел понять, но оно мне не нравится»…
    В моем текущем проекте используется эта злосчастная Model First, а точнее DB First, ничего ужаснее себе представить нельзя. Самая прелесть начинается когда несколько разработчиков правят этот ужосс одновременно. Модель при этом выглядит как месиво этих самых диаграмм.
    Статье минус.


    1. chumakov-ilya
      08.07.2015 21:30

      Не стоит и пытаться сливать изменения в EDMX. Проще удалить-добавить нужные куски на основе актуальной БД.
      Одновременное редактирование EDMX разумнее запретить в принципе, до сих пор скучаю по опции из старых версий TFS «prevent from check out».


    1. asanych Автор
      09.07.2015 07:58
      +1

      Поясните, пожалуйста, вашу мысль. Я тоже недоволен этой реализацией. Я где-то написал, что мне нравится MF в EF? Комментирующие также все как один убеждают, что единственно верный способ — это CF.
      Если мы с вами согласны, то чем в статье вы недовольны?


      1. mayorovp
        09.07.2015 08:37

        Мы все недовольны тем, что выдаете недостатки EF MF за недостатки EF вообще.