Работаю сейчас над двумя проектами на C#, где есть базы данных с кучей хранимых процедур, а для доступа к данным используются какие-то обёртки для выполнения этих самых процедур. Один проект — legacy, второй свеженаписанный.
Оно как бы работает, но есть определённые проблемы. Из наиболее значимых проблем — трудности написания юнит-тестов для хранимых процедур (на данный момент, если не ошибаюсь, у нас нет ни одного юнит-теста для SQL-кода) и сложность поддержки и развития всего этого великолепия. Сейчас на legacy-проекте есть полтора разработчика, которые хорошо ориентируются в имеющихся SQL-процедурах, а для остальных (и меня в том числе) любое исправление логики в SQL выливается как минимум в час раскуривания, "а как же оно работает". Размеры хранимых процедур достигают нескольких сотен строк, внутри там бывает десяток JOIN'ов, CROSS APPLY и прочих премудростей, которые при беглом прочтении кода понять трудно. История последних найденных багов показывает, что SQL-код исправлять приходится довольно часто (бизнес-правила часто меняются и добавляются), а делать это сложно и долго.
Хочу предложить коллегам мигрировать на Entity Framework. С тем, чтобы сначала использовать его для вызова имеющихся хранимых процедур и постепенно, по мере доработки кода, переписывать хранимые процедуры на LINQ-запросы. Ну это чтобы не ломать то, что есть и не тратить полгода на переписывание с нуля. Чтобы миграция была плавной и наименее болезненной. Сейчас готовлю документ с моими предложениями и описанием предлагаемой процедуры миграции. В конце хочется полностью избавиться от бизнес-логики в SQL-коде. Это в идеале.
Так вот. Плюсы использования Entity Framework'а я примерно знаю. А какие у него есть минусы? Какие могут быть доводы против использования Entity Framework? Я слышал, что он несколько уступает по производительности прямым SQL-запросам, но разница исчисляется единицами процентов — пару-тройку процентов производительности мы можем принести в жертву плюсам этого фреймворка. Если где-то будет сильно хуже, то там может оставить пару-тройку хранимых процедур и вызывать их через EF. Юниттестирование с ним тоже не столь просто, но хотябы решаемо — мне уже приходилось писать подобие юнит-тестов для EF, где использовалась временная база данных на реальном SQL-сервере. Это работает, но медленно. А поиски по интернету показывают, что для EF можно создавать базы в памяти — например, используя SQLite.
Какие ещё минусы есть у Entity Framework? Что может послужить аргументом для отказа от его использования в проекте?
На днях я начал писать проект. В проекте есть база MSSQL. Entity Framework не использую, потому что у меня есть некий негативный опыт разбирательства с ним, при попытке подключить его к Постгресу.
решил, что если уж знаний SQL у меня достаточно для написания развесистых хранимок для Постгреса, то тяжелый ORM мне не нужен. Использую linq2db, базу пишу руками, хотя — code first для linq2db не помешал бы. Пока все хорошо. Единственное, я не смог заставить работать шаблон linqdb для генерации модели по базе, что-то ему там с путями в шаблоне не нравится. Таким образом, маппинг тоже пишу руками, благо — база еще маленькая.
Преимущество хранимок вижу в том, что обновление логики в хранимке — один запуск скрипта в консоли БД. Обновление атомарное, вступает в действие моментально. Как сделать подобное же обновление в сервере приложений, не обновляя все приложение и не перезапуская его — пока не знаю.
Также, при работе приложений с базой, в двухзвенке в чем-то упрощается их взаимодействие. Сайт на PHP пишет в базу, десктопное приложение на Delphi читает из базы, сервис на Яве работает с этой же базой. Если делать некую прослойку, и засовывать логику в нее, а не в базу, то придется в этой же прослойке решать:
1) Как передавать данные. В прямом смысле — как. Также база может рассылать обновления сама, в постгресе это listen-notify, подобный механизм нужно будет реализовывать в приложении, изобретать протокол или использовать готовый (какой? чтобы его и php понимал, и ява, и дестктопное приложение c#, которым заменят приложение на Delphi)
2) Как делать разделение доступа и логины-пароли. В базе это все есть. Можно даже row-level security сделать.
3) Что делать с обновлением уже изменившихся данных. В базе это просто поле timestamp, соответственно пишем update... where... and ts = :stamp. Следует ли тянуть таймштампы на уровень приложения и отдавать их клиентам, или надо изобретать что-то еще?
В общем, надо писать, а потом еще и писать, и еще писать. То, что в коде СУБД уже давно написано.
Так же я бы на вашем месте присмотрелся бы к https://github.com/igor-tkachev/bltoolkit .
Сам не работал(только собираюсь), но судя по всему очень крутая вещь. Ну и как минимум комьюнити не далеко(в соседней ветке форума ).
Здравствуйте, Artem Korneev, Вы писали:
AK>Какие ещё минусы есть у Entity Framework? Что может послужить аргументом для отказа от его использования в проекте?
Личный хит-парад:
1) нельзя навесить хинты. Это вообще-то не правда, но люди упорно повторяют эту глупость.
2) генерирует плохие запросы. Правда известен ровно один случай плохих запросов — при использовании навигационных свойств и то можно обойти. В среднем генерирует запросы лучше людей.
3) медленный маппинг. По замерам около 0,2 микросекунды на объект, но видимо слишком много для некоторых.
4) медленная генерация запроса. Бывает, но запрос кэшируется и генерация происходит один раз за время работы приложения.
5) нельзя использовать все возможности sql. но никто не мешает сделать функцию и замапить её через ef или напрмую вызвать sql через ef.
Здравствуйте, Artem Korneev, Вы писали:
AK>Хочу предложить коллегам мигрировать на Entity Framework. С тем, чтобы сначала использовать его для вызова имеющихся хранимых процедур и постепенно, по мере доработки кода, переписывать хранимые процедуры на LINQ-запросы. Ну это чтобы не ломать то, что есть и не тратить полгода на переписывание с нуля. Чтобы миграция была плавной и наименее болезненной. Сейчас готовлю документ с моими предложениями и описанием предлагаемой процедуры миграции. В конце хочется полностью избавиться от бизнес-логики в SQL-коде. Это в идеале.
Для миграции старого проекта возьми linq2db. Он работает быстрее ef, маппинг даже быстрее dapper. Он больше ориентирован работу с базой с помощью запросов — легче будет существующие запросы переписать.
Для нового проекта бери ef с миграциями. это позволит очень быстро развивать приложение.
Здравствуйте, Artem Korneev, Вы писали:
AK>Какие могут быть доводы против использования Entity Framework?
У меня довод простой: потому что уже есть BLToolkit! Простой, надёжный, маленький и полностью контролируемый.
ORM само по себе — хорошая идея, но доведённая до абсурда, воплотилась в виде EF. BLToolkit — это та граница, на которой надо было остановиться.
В целом вопрос не правильно сформулирован, так как проводится неявная параллель "не писать развесистые хранимки" == "использовать EF".
На самом же деле, во-первых и от хранимок/вью даже сейчас есть польза, вне зависимости от того используется ли какой-нибудь ORM, а во-вторых — одним EF-ом набор доступных средств не ограничивается.
В кратце — да, держать бизнес-логику в SQL — не самая лучшая идея, но на практике разработчик выбирает тот инструмент, который ему привычнее. Поэтому даже начиная самый новый проект, разработчик, который в состоянии виртуозно выпиливать по SQL-ю, естественно возьмет SQL. (это, по сути, и есть ответ на вопрос)
Что же касается EF — то среди всех ORM фреймворков, доступных на данный момент — это один из наихудших вариантов. Во-первых, потому что изначально был написан ужасно криво, а во-вторых, потому что текущее направление работ наконец-то закрыли и следующая версия будет написана с нуля, при этом даже сохранение API не гарантируется.
К счастью, есть достаточное количество достойных альтернатив, от linq2SQL (если нужно ограничиться стеком MS) до linq2db.
IB>К счастью, есть достаточное количество достойных альтернатив, от linq2SQL (если нужно ограничиться стеком MS) до linq2db.
Всё же давайте перечислим достойный альтернативы
linq2SQL
linq2db
что еще?
NHibernate?
еще что достойное?
А что не из стека MS? Там же LINQ нету. Разве можно без LINQ?
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, Artem Korneev:
IB>К счастью, есть достаточное количество достойных альтернатив, от linq2SQL (если нужно ограничиться стеком MS) до linq2db.
Linq2SQL работает медленнее EF, не поддерживает Async, не поддерживает codefirst, автоматом не синхронизирует модель с базой. Называть это достойно альтернативой — надо иметь очень сильную веру.
Здравствуйте, gandjustas, Вы писали:
G>Linq2SQL работает медленнее EF,
Ну, это просто не правда — работает он ни чуть не медленнее, во многих местах существенно быстрее, просто за счет того, что умеет генерить адекватный SQL, и не содержит тонну лишнего кода.
G> не поддерживает Async, не поддерживает codefirst,
К счастью.
G>автоматом не синхронизирует модель с базой.
А это вообще его преимущество над EF.
G>Называть это достойно альтернативой — надо иметь очень сильную веру.
Надо соизмерять задачи и средства, а не бросаться оголтело на все что придет в воспаленный мозг некоторых архитекторов.
Здравствуйте, Alexander Polyakov, Вы писали:
AP>linq2SQL AP>linq2db AP>что еще?
Вполне достаточно. iBATIS, LLBNGen в свое время так же были не плохи и LINQ вроде поддерживали — не знаю как сейчас. В целом выбрать есть из чего.
AP>NHibernate?
Тот же EF, только в профиль.
AP>А что не из стека MS? Там же LINQ нету.
Как это нет? вот linq2db — не от MS, а linq есть лучше чем у MS даже.
AP>Разве можно без LINQ?
Можно, но не нужно.
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, gandjustas, Вы писали:
G>>Linq2SQL работает медленнее EF, IB>Ну, это просто не правда — работает он ни чуть не медленнее, во многих местах существенно быстрее, просто за счет того, что умеет генерить адекватный SQL, и не содержит тонну лишнего кода.
У Linq2SQL отсутствует кеширование сгенерированного SQL, он каждый раз честно его генерит.
У Linq2SQL медленнее работает мапинг.
По поводу запросов — только один кейс для EF, где он плохой запрос генерит, и то он элементрано обходится через ручной джоин.
Так что неправду ты пишешь.
G>> не поддерживает Async, не поддерживает codefirst, IB>К счастью.
К твоему — может быть и да. Но для разработчиков — лишний гемор.
G>>автоматом не синхронизирует модель с базой. IB>А это вообще его преимущество над EF.
EF тоже не синхронизирует в режиме Database First.
Зато умеет базу генерить из codefirst.
G>>Называть это достойно альтернативой — надо иметь очень сильную веру. IB>Надо соизмерять задачи и средства, а не бросаться оголтело на все что придет в воспаленный мозг некоторых архитекторов.
Надо как минимум быть честным.
Здравствуйте, gandjustas, Вы писали:
G>У Linq2SQL отсутствует кеширование сгенерированного SQL, он каждый раз честно его генерит.
И это тоже не правда — в linq2SQL кеширование присутствует.
G>У Linq2SQL медленнее работает мапинг.
Не медленне чем в EF, да и не это вносит основные тормоза — по сравнению с плохим запросом тормоза маппинга это просто другой порядок малости.
G>По поводу запросов — только один кейс для EF, где он плохой запрос генерит, и то он элементрано обходится через ручной джоин.
Какая прелесть... ж)) Один кейс? да это я просто ногтем ковырнул.
Ничего, что этот кейс — больше половины всех запросов к базе в обычном приложении? Ты предлагаешь каждый left join в ручную выпиливать? Зачем тогда вообще ORM нужен?
G>К твоему — может быть и да. Но для разработчиков — лишний гемор.
А джойны в ручную выпиливать — не гемор? Я уж лучше без генератора объектов проживу, чем без нормального генератора запросов.
G>Надо как минимум быть честным.
Я уже вижу кто здесь честный.
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, gandjustas, Вы писали:
G>>У Linq2SQL отсутствует кеширование сгенерированного SQL, он каждый раз честно его генерит. IB>И это тоже не правда — в linq2SQL кеширование присутствует.
С какой версии? Я крайний раз в 4.0 профилировал приложение c Linq2SQL видел постоянную перекомпиляцию запросов.
Может он кеширует запросы в рамках одного контекста, но это вообще не кеширование, а хрень.
G>>У Linq2SQL медленнее работает мапинг. IB>Не медленне чем в EF, да и не это вносит основные тормоза — по сравнению с плохим запросом тормоза маппинга это просто другой порядок малости.
У тебя замеры есть?
G>>По поводу запросов — только один кейс для EF, где он плохой запрос генерит, и то он элементрано обходится через ручной джоин. IB>Какая прелесть... ж)) Один кейс? да это я просто ногтем ковырнул.
И? Ковырни еще. А то из-за одного кейса, который легко обходится, столько шума...
Да еще и impact небольшой, дополнительный sort в конце сделается, который на небольшом кол-ве строк никаких проблем не приносит.
IB>Ничего, что этот кейс — больше половины всех запросов к базе в обычном приложении? Ты предлагаешь каждый left join в ручную выпиливать? Зачем тогда вообще ORM нужен?
Типы проверять, руками джоин написать не архи-проблема, хоть явно видишь где будет сложный запрос.
G>>К твоему — может быть и да. Но для разработчиков — лишний гемор. IB>А джойны в ручную выпиливать — не гемор? Я уж лучше без генератора объектов проживу, чем без нормального генератора запросов.
Парни в SO нормально так жили, не вижу проблем.
IB>Вполне достаточно. iBATIS, LLBNGen в свое время так же были не плохи и LINQ вроде поддерживали — не знаю как сейчас. В целом выбрать есть из чего.
iBATIS вроде не был испорчен linq-ом.
За ссылку спасибо. Я пошёл читать тот холивар.. Второй день разбираю.
А>Преимущество хранимок вижу в том, что обновление логики в хранимке — один запуск скрипта в консоли БД. Обновление атомарное, вступает в действие моментально. Как сделать подобное же обновление в сервере приложений, не обновляя все приложение и не перезапуская его — пока не знаю.
У меня ситуация немного сложнее. Серверов несколько, причём на одном из проектов эти сервера работают они на разных версиях приложения и, соответственно, с разными версиями SQL-кода. Это вызвано тем, что обновлением серверов занимаются те команды, которые используют наше приложение и не всегда у них есть время и желание обновляться.
А>Также, при работе приложений с базой, в двухзвенке в чем-то упрощается их взаимодействие. Сайт на PHP пишет в базу, десктопное приложение на Delphi читает из базы, сервис на Яве работает с этой же базой. Если делать некую прослойку, и засовывать логику в нее, а не в базу, то придется в этой же прослойке решать:
[..skip..]
Ну этих проблем у меня нет. Обновления данных в реальном времени мне не нужно, доступа из разных языков тоже, разделение уровней доступа реализовано на уровне приложения (ASP.NET). Я хочу лишь заменить слой доступа к данным.
Здравствуйте, gandjustas, Вы писали:
G>1) нельзя навесить хинты. Это вообще-то не правда, но люди упорно повторяют эту глупость.
А "хинт" это что?
G>2) генерирует плохие запросы. Правда известен ровно один случай плохих запросов — при использовании навигационных свойств и то можно обойти. В среднем генерирует запросы лучше людей.
Навигационные свойства это что? Это "курсоры", или что-то другое?
G>5) нельзя использовать все возможности sql. но никто не мешает сделать функцию и замапить её через ef или напрмую вызвать sql через ef.
Да, про вызов хранимых процедур из EF я знаю, но в идеале я хотел бы избавиться от хранимых процедур полностью.
А какие именно возможности SQL нельзя использовать через EF?
Здравствуйте, gandjustas, Вы писали:
G>Для миграции старого проекта возьми linq2db. Он работает быстрее ef,
А насколько быстрее и в каких случаях?
Ну т.е... если linq2db в каких-то случаях заметно быстрее EF, то отсюда неявно следует, что EF в этих же случаях заметно медленнее прямого SQL, так? Вот это мне сейчас в первую очередь и интересно — в каких случаях, почему и как с этим бороться. Выбор linq2db как один из обходных путей — добавлю в копилку, нужно будет пощупать его поближе, может быть и на него переползём.
G>маппинг даже быстрее dapper.
Маппинг выполняется при чтении данных из базы? Или это что-то другое? В EF он медленный?
Здравствуйте, IB, Вы писали:
IB>В целом вопрос не правильно сформулирован,
Мой вопрос — о минусах EF.
IB>так как проводится неявная параллель "не писать развесистые хранимки" == "использовать EF".
Это неявная параллель — для моей ситуации. Хранимые процедуры у нас уже развесистые и как убедить разработчиков писать их короткими и понятными — я не знаю. Говорил на эту тему несколько раз. И с разработчиками и с тимлидом, а воз и ныне там. Переход на EF я рассматриваю как шанс избавиться от хранимых процедур вообще.
IB>на практике разработчик выбирает тот инструмент, который ему привычнее
Те, кто делал этот выбор, давно уже свалили в другие команды. Года два назад, если не больше. А сейчас есть два разработчика, которые вроде раскурили как оно написано и их это, в принципе, устраивает. И ещё два-три разработчика, которых добавили в проект сравнительно недавно и у которых мозги пухнут при попытке что-то там добавить. Про EF разработчики знают, но похоже, что кроме меня активно им никто не пользовался. Потому хочу предложить рассмотреть возможность миграции.
IB>Поэтому даже начиная самый новый проект, разработчик, который в состоянии виртуозно выпиливать по SQL-ю, естественно возьмет SQL. (это, по сути, и есть ответ на вопрос)
Это ответ на какой-то другой вопрос.
Мой вопрос — про слабые стороны EF. Интересуюсь заранее, чтоб не получилось, что мы на него мигрируем, всё станет ещё хуже и коллеги закидают меня гнилыми помидорами.
IB>Что же касается EF — то среди всех ORM фреймворков, доступных на данный момент — это один из наихудших вариантов. Во-первых, потому что изначально был написан ужасно криво, а во-вторых, потому что текущее направление работ наконец-то закрыли и следующая версия будет написана с нуля, при этом даже сохранение API не гарантируется.
А откуда эта информация?
Я поискал, пока ничего не нашёл про то, что новая версия будет писаться с нуля. Зато нашёл интересный обзор проблем производительности -- http://msdn.microsoft.com/en-US/data/hh949853
Текста много. Пока сам разбираюсь, насколько всё хорошо или плохо и будет ли это критично для нашего случая.
IB>К счастью, есть достаточное количество достойных альтернатив, от linq2SQL (если нужно ограничиться стеком MS) до linq2db.
Да.. на альтернативы тоже посмотрим, но это если EF чем-то отпугнёт. Пока хочется разобраться с ним -- будет ли что-то мешать нормально использовать его на наших задачах.