Здравствуйте, IT, Вы писали:
IT>От хранимок нужно избавлятся при первом удобном случае. Логика в БД — это сегодня нонсенс.
Красивый сказка, которая не выдерживает проверку практикой. При большее детальном рассмотрении запросов, создаваемых ORM выясняется что сильно не эффективны. Нужно избавляться от кодогенратов во всех их проявленяих в том числе и от ORM.
Здравствуйте, Gattaka, Вы писали:
G>Здравствуйте, IT, Вы писали:
IT>>От хранимок нужно избавлятся при первом удобном случае. Логика в БД — это сегодня нонсенс. G>Красивый сказка, которая не выдерживает проверку практикой. При большее детальном рассмотрении запросов, создаваемых ORM выясняется что сильно не эффективны. Нужно избавляться от кодогенратов во всех их проявленяих в том числе и от ORM.
А можно пример? На моей практике люди сильно хуже пишут запросы в sql, чем на linq. Тем более последний отсекает ряд банальных ошибок, из-за которых запросы могут тормозить.
Здравствуйте, gandjustas, Вы писали:
G>>Красивый сказка, которая не выдерживает проверку практикой. При большее детальном рассмотрении запросов, создаваемых ORM выясняется что сильно не эффективны. Нужно избавляться от кодогенратов во всех их проявленяих в том числе и от ORM. G>А можно пример? На моей практике люди сильно хуже пишут запросы в sql, чем на linq. Тем более последний отсекает ряд банальных ошибок, из-за которых запросы могут тормозить.
Привести пример это целое дело. Попробую кратко.
ORM — NHibernate. Есть таблица узлов, они по дискриминатору разделены на два класса. Между узлами есть связи, что хранятся в отдельной таблице. Я выбираю такие связи у которых с одной стороны узел определенного типа, а также узлы с определенным свойством. У ORM получается 4 join, хотя можно было бы обойтись 2-мя с and в условии. Пришлось изголятся и использовать устаревший синтаксис. Там получилось нормальное количество join, но при этом вытягиваются данные не только по связям, но и по узлам... Вот такая альтернатива.
Здравствуйте, Gattaka, Вы писали:
G>Здравствуйте, gandjustas, Вы писали:
G>>>Красивый сказка, которая не выдерживает проверку практикой. При большее детальном рассмотрении запросов, создаваемых ORM выясняется что сильно не эффективны. Нужно избавляться от кодогенратов во всех их проявленяих в том числе и от ORM. G>>А можно пример? На моей практике люди сильно хуже пишут запросы в sql, чем на linq. Тем более последний отсекает ряд банальных ошибок, из-за которых запросы могут тормозить.
G>Привести пример это целое дело. Попробую кратко. G>ORM — NHibernate. Есть таблица узлов, они по дискриминатору разделены на два класса. Между узлами есть связи, что хранятся в отдельной таблице. Я выбираю такие связи у которых с одной стороны узел определенного типа, а также узлы с определенным свойством. У ORM получается 4 join, хотя можно было бы обойтись 2-мя с and в условии. Пришлось изголятся и использовать устаревший синтаксис. Там получилось нормальное количество join, но при этом вытягиваются данные не только по связям, но и по узлам... Вот такая альтернатива.
Приведите код, так не понятно о чем речь. Скорее всего вы слишком много игрались моделированием в NHibernate, поэтому такой результат.
Здравствуйте, gandjustas, Вы писали:
G>Здравствуйте, Gattaka, Вы писали:
G>>Здравствуйте, IT, Вы писали:
IT>>>От хранимок нужно избавлятся при первом удобном случае. Логика в БД — это сегодня нонсенс. G>>Красивый сказка, которая не выдерживает проверку практикой. При большее детальном рассмотрении запросов, создаваемых ORM выясняется что сильно не эффективны. Нужно избавляться от кодогенратов во всех их проявленяих в том числе и от ORM.
G>А можно пример? На моей практике люди сильно хуже пишут запросы в sql, чем на linq. Тем более последний отсекает ряд банальных ошибок, из-за которых запросы могут тормозить.
Здравствуйте, Sharov, Вы писали:
S>Здравствуйте, gandjustas, Вы писали:
G>>Здравствуйте, Gattaka, Вы писали:
G>>>Здравствуйте, IT, Вы писали:
IT>>>>От хранимок нужно избавлятся при первом удобном случае. Логика в БД — это сегодня нонсенс. G>>>Красивый сказка, которая не выдерживает проверку практикой. При большее детальном рассмотрении запросов, создаваемых ORM выясняется что сильно не эффективны. Нужно избавляться от кодогенратов во всех их проявленяих в том числе и от ORM.
G>>А можно пример? На моей практике люди сильно хуже пишут запросы в sql, чем на linq. Тем более последний отсекает ряд банальных ошибок, из-за которых запросы могут тормозить.
S>По-моему наоборт -- на sql можно четко сформулировать запрос без всяческих подпрыжек
Нельзя, если запрос параметризованный.
типичный случай для приложения — отображение списка с фильтрами — 3 фильтра+постраничная разбивка. Фильтры по умолчанию не фильтруют ничего и могут быть выбраны в любой комбинации. Linq сгенерирует 8 или 16 разных запросов для различных сочетаний фильтров и страниц.
На SQL будет написан один запрос с тремя условиями примерно такого вида:
where 1=1
and (t.Field1 = @p1 or @p1 is null)
and (t.Field2 = @p2 or @p2 is null)
and (t.Field3 = @p3 or @p3 is null)
План запроса в этом случае будет далек от оптимального и подвержен parameter sniffing problem.
В реальности еще добавится сортировка и разные проекции в запросах с одинаковыми предикатами. Общее количество возможных запросов перевалит за 50. При этом такой запрос будет на главной странице и должен работать максимально быстро. Linq тут очень поможет, а в SQL это будет АД.
S>вот с linq недавно огрбеб -- http://docs.telerik.com/data-access/developers-guide/profiling-and-tuning/profiler-and-tuning-advisor/data-access-profiler-n-plus-one-problem
lazy load надо отключать всегда и лучше не пользоваться ORM где он включен. В EF он отключен по-умолчанию для code-first, а в linq2db его вовсе нет.
Здравствуйте, Gattaka, Вы писали:
IT>>От хранимок нужно избавлятся при первом удобном случае. Логика в БД — это сегодня нонсенс. G>Красивый сказка, которая не выдерживает проверку практикой.
Не знаю какая у тебя практика, а я последние 10 лет занимаюсь в осном изничтожением сохранённых процедур в стиле инквизиции. Увидел, поймал, сразу на костёр.
G>При большее детальном рассмотрении запросов, создаваемых ORM выясняется что сильно не эффективны. Нужно избавляться от кодогенратов во всех их проявленяих в том числе и от ORM.
Это вы, батенька, готовить не умеете. Выкинь все свои ORM на помойку и возьми ту, которая генерирует только то, что ей сказано.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Gattaka, Вы писали:
G>А не могли бы вы приблизительную сигнатуру вашего класса показать? Так чтобы точно понимать...
Не мог бы. Там и сигнатуры особенной нет. Это просто класс без всяких интерфейсов и паттернов, в котором куча коллекций с данными. Методы для инициализации и ещё какой-то фигни по мелочи. Просто большой контейнер. Это его основная задача.
Если нам не помогут, то мы тоже никого не пощадим.
G>типичный случай для приложения — отображение списка с фильтрами — 3 фильтра+постраничная разбивка. Фильтры по умолчанию не фильтруют ничего и могут быть выбраны в любой комбинации. Linq сгенерирует 8 или 16 разных запросов для различных сочетаний фильтров и страниц. G>На SQL будет написан один запрос с тремя условиями примерно такого вида: G>
G>where 1=1
G>and (t.Field1 = @p1 or @p1 is null)
G>and (t.Field2 = @p2 or @p2 is null)
G>and (t.Field3 = @p3 or @p3 is null)
G>
G>План запроса в этом случае будет далек от оптимального и подвержен parameter sniffing problem.
G>Тут подробно http://blog.gandjustas.ru/2014/09/23/asp.net-linq-ef-sql-server-performance/
G>В реальности еще добавится сортировка и разные проекции в запросах с одинаковыми предикатами. Общее количество возможных запросов перевалит за 50. При этом такой запрос будет на главной странице и должен работать максимально быстро. Linq тут очень поможет, а в SQL это будет АД.
Не верю. Во-первых, надо смотреть на план linq запросов, которых аж целых 8 вместо 1. Во-вторых, спорно ибо нормальный спец. в рбд. напишет может написать запрос явно не хуже чем чем linq сгенеренный.
Здравствуйте, Sharov, Вы писали:
S>Здравствуйте, gandjustas, Вы писали:
G>>типичный случай для приложения — отображение списка с фильтрами — 3 фильтра+постраничная разбивка. Фильтры по умолчанию не фильтруют ничего и могут быть выбраны в любой комбинации. Linq сгенерирует 8 или 16 разных запросов для различных сочетаний фильтров и страниц.
S>Не верю.
Можешь проверить
S>Во-первых, надо смотреть на план linq запросов, которых аж целых 8 вместо 1.
Тогда идея с рукопашными запросами покажется совсем глупой. Потому что у каждого запроса из 8 будет свой оптимальный план.
А рукопашный запрос вида
where 1=1
and (t.Field1 = @p1 or @p1 is null)
and (t.Field2 = @p2 or @p2 is null)
and (t.Field3 = @p3 or @p3 is null)
Будет иметь план, который оптимален только для одного из 8 сочетаний параметров. Чем больше параметров тем хуже, никакие индексы не помогут. Поможет только option (recompile), но добавит затраты на компиляцию запроса каждый раз.
Погугли советы использовать option (recompile), почти везде вместо него можно использовать генератор запросов.
S>Во-вторых, спорно ибо нормальный спец. в рбд. напишет может написать запрос явно не хуже чем чем linq сгенеренный.
Нормальный спец напишет запрос явно не хуже, чем linq. ОДИН запрос.
Поддерживать самописный генератор или несколько десятков однотипных запросов даже "нормальный спец" не сможет.
Здравствуйте, gandjustas, Вы писали:
G>Здравствуйте, Sharov, Вы писали:
S>>Здравствуйте, gandjustas, Вы писали:
G>>>типичный случай для приложения — отображение списка с фильтрами — 3 фильтра+постраничная разбивка. Фильтры по умолчанию не фильтруют ничего и могут быть выбраны в любой комбинации. Linq сгенерирует 8 или 16 разных запросов для различных сочетаний фильтров и страниц.
S>>Не верю. G>Можешь проверить
S>>Во-первых, надо смотреть на план linq запросов, которых аж целых 8 вместо 1. G>Тогда идея с рукопашными запросами покажется совсем глупой. Потому что у каждого запроса из 8 будет свой оптимальный план. G>А рукопашный запрос вида G>
G>where 1=1
G>and (t.Field1 = @p1 or @p1 is null)
G>and (t.Field2 = @p2 or @p2 is null)
G>and (t.Field3 = @p3 or @p3 is null)
G>
G>Будет иметь план, который оптимален только для одного из 8 сочетаний параметров. Чем больше параметров тем хуже, никакие индексы не помогут. Поможет только option (recompile), но добавит затраты на компиляцию запроса каждый раз.
G>Погугли советы использовать option (recompile), почти везде вместо него можно использовать генератор запросов.
А linq то как поможет?
S>>Во-вторых, спорно ибо нормальный спец. в рбд. напишет может написать запрос явно не хуже чем чем linq сгенеренный. G>Нормальный спец напишет запрос явно не хуже, чем linq. ОДИН запрос. G>Поддерживать самописный генератор или несколько десятков однотипных запросов даже "нормальный спец" не сможет.
Вы смеетесь ??? Т.е. если Вы возьмете человека на фул-тайм с задачей написания sql запросов (специалиста), он больше одного запроса написать не сможет? Не считая того, что вообще говоря это должно быть компетенцией разработчика, отв. за соотв. таблицы (сущности).
S>>>>вот с linq недавно огрбеб -- http://docs.telerik.com/data-access/developers-guide/profiling-and-tuning/profiler-and-tuning-advisor/data-access-profiler-n-plus-one-problem G>>>lazy load надо отключать всегда и лучше не пользоваться ORM где он включен. В EF он отключен по-умолчанию для code-first, а в linq2db его вовсе нет. S>>да-да-да, и сразу подгружать всю базу данных со всеми join'ами, которые не нужны. G>Ты о чем сейчас?
Ну как-бы у openaccess telerik'овского допустим есть eager loading и laze loading (как и везде). При подгрузке родительских сущностей все дочерние сущности соотв родительской тоже подгрузятся, даже если они не нужны.
Здравствуйте, gandjustas, Вы писали:
S>>Во-первых, надо смотреть на план linq запросов, которых аж целых 8 вместо 1. G>Тогда идея с рукопашными запросами покажется совсем глупой. Потому что у каждого запроса из 8 будет свой оптимальный план. G>А рукопашный запрос вида G>
G>where 1=1
G>and (t.Field1 = @p1 or @p1 is null)
G>and (t.Field2 = @p2 or @p2 is null)
G>and (t.Field3 = @p3 or @p3 is null)
G>
G>Будет иметь план, который оптимален только для одного из 8 сочетаний параметров. Чем больше параметров тем хуже, никакие индексы не помогут. Поможет только option (recompile), но добавит затраты на компиляцию запроса каждый раз.
G>Погугли советы использовать option (recompile), почти везде вместо него можно использовать генератор запросов.
Во первых это не такие уж большие затраты, потом еще не известно что более затратно перекомпилировать один запрос. Или хранить N-атцать планов запросов и тоже их перекомпилировать при обновлении статистики, например.
И давайте от противного. А может ли ваш linq добавить этот option (recompile)? Нет не может — будет кучу запросов генерить
По кейсу что вы описали: На SQL вы можете разбить запрос на 2 подзапроса, например с помощью табличных выражений. Можете добавить view, затем ее проиндексировать. Все зависит от конкретной системы и т.п. Точно так же никто не отменял динамический sql sp_exec... Вы можете сформировать строчку запроса, а потом ее выполнить. Прямо как linq.
Здравствуйте, IT, Вы писали:
B>>Задача любого архитектурного паттерна — фрагментация сложности. IT>При условии, что архитектурные решения не вносят ещё больше проблем, чем решают.
Разумеется.
B>>Паттерны DAL отделяют сложность бизнес-логики от сложности работы с хранилищем данных с помощью разного вида интерфейсов. IT>Не совсем так. DAL изолирует работу с базой в терминах самой базы от остальной части приложения путём конвертации терминов приложения в термины БД и наоборот.
Ну в общем, да. Можно еще короче сказать: абстрагируют работу с хранилищем, причем не обязательно БД: например, вы обрабатываете какие-то документы и изначально храните их в блобах в БД, потом переносите в облачное хранилище типа Amazon S3 — если оптимизация за счет отдачи прямых ссылок на хранилище невозможна (например, потому что клиент в интернете, а хранилище в VPN) или не требуется (например, потому что клиенту нужны результаты обработки, а не сами документы), то интерфейс DAL и бизнес-логику можно при этом не менять.
Здравствуйте, Sharov, Вы писали:
G>>А рукопашный запрос вида G>>
G>>where 1=1
G>>and (t.Field1 = @p1 or @p1 is null)
G>>and (t.Field2 = @p2 or @p2 is null)
G>>and (t.Field3 = @p3 or @p3 is null)
G>>
G>>Будет иметь план, который оптимален только для одного из 8 сочетаний параметров. Чем больше параметров тем хуже, никакие индексы не помогут. Поможет только option (recompile), но добавит затраты на компиляцию запроса каждый раз.
S>А linq то как поможет?
В итоге в зависимости от параметров будет 8 запросов, каждый с оптимальным планом под свое сочетание параметров. Каждый из 8 запросов может быть оптимизирован своим индексом.
S>Вы смеетесь ??? Т.е. если Вы возьмете человека на фул-тайм с задачей написания sql запросов (специалиста), он больше одного запроса написать не сможет? Не считая того, что вообще говоря это должно быть компетенцией разработчика, отв. за соотв. таблицы (сущности).
Конечно сможет. И два сможет, и десять и может даже 50. Для статистики — у меня в очень простом приложении с 5 таблицами в базе получилось суммарно 60 разных запросов. А нанимать спеца отдельно по БД для базы менее чем два десятка таблиц никто не будет. Вот и посчитай какое количество запросов ему надо будет поддерживать.
S>Ну как-бы у openaccess telerik'овского допустим есть eager loading и laze loading (как и везде). При подгрузке родительских сущностей все дочерние сущности соотв родительской тоже подгрузятся, даже если они не нужны.
Выкини это говно и возьми linq2db или ef. Они грузят ровно то, что скажешь.
Здравствуйте, Gattaka, Вы писали:
G>>Погугли советы использовать option (recompile), почти везде вместо него можно использовать генератор запросов. G>Во первых это не такие уж большие затраты, потом еще не известно что более затратно перекомпилировать один запрос. Или хранить N-атцать планов запросов и тоже их перекомпилировать при обновлении статистики, например.
Конечно перекомпиляция на каждый вызов более затрата если вызовов больше чем N, где N — количество запросов сгенерированных linq. N обычно не превышает 15. То есть если у тебя запрос вызывается 15 и более раз, то выгоднее использовать linq (или другой генератор), а не option recompile. Хранение планов не стоит ничего, потому что планов всегда ограниченное количество и надо сервак подобрать, чтобы plan cache не вытеснялся.
G>И давайте от противного. А может ли ваш linq добавить этот option (recompile)? Нет не может — будет кучу запросов генерить
Правильно. Генерить кучу запросов выгоднее. Хотя наверное linq2db может, я не проверял.
G>По кейсу что вы описали: На SQL вы можете разбить запрос на 2 подзапроса, например с помощью табличных выражений.
Все равно план запроса будет один, со всеми вытекающими.
G>Можете добавить view, затем ее проиндексировать.
Индексированные view точно также можно и в linq использовать.
G>Все зависит от конкретной системы и т.п. Точно так же никто не отменял динамический sql sp_exec... Вы можете сформировать строчку запроса, а потом ее выполнить. Прямо как linq.
Да-да, давайте клеить строки руками, а не пользоваться гарантиями компилятора.
Опытный разработчик sql склейку строк делает один раз, а потом не связывается с такой архитектурой.
Здравствуйте, gandjustas, Вы писали:
G>>И давайте от противного. А может ли ваш linq добавить этот option (recompile)? Нет не может — будет кучу запросов генерить G>Правильно. Генерить кучу запросов выгоднее. Хотя наверное linq2db может, я не проверял.
Выгоднее это да, но насколько? План запроса компилируется пару милисекунд, можно для каждого конкретного посмотреть в плане. И на чем мы экономим? Если сам запрос выполняется пару секунд. Ну... если хорошо скомпилировался. Если плохо — полчаса. Здесь баланс нужно соблюдать. На самом деле что ORM хорошо делает, это простейший CRUD, самый простейший. И сценарий что вы описали. Но это по факту Read.
G>>По кейсу что вы описали: На SQL вы можете разбить запрос на 2 подзапроса, например с помощью табличных выражений. G>Все равно план запроса будет один, со всеми вытекающими.
G>>Можете добавить view, затем ее проиндексировать. G>Индексированные view точно также можно и в linq использовать.
Ну и что это за уровень абстракции в таком случае? Это всего лишь способ писать SQL на C#. Извращение...
Здравствуйте, another_coder, Вы писали:
_>Удивляют люди, которые считают, что правильный репозиторий должен выголядить как этот интерфейс
Я сам был таким и писал такой код, однако через некоторое время он переставал писаться и всё становилось очень криво. Когда код пишется с трудом, для меня это знак что что-то я делаю не так. _>Это ни что иное, как реализация DAO паттерна. Сам же репозиторий может совершенно не знать о механике и деталях хранения данных (база, файлы, сервис). На маленьких проектах разницу не так сильно видно. А на больших, когда помимо самих запросов, еще необходимо прикрутить кеширование, логирование, фильтрацию по служебным полям, вдруг оказывается, что репозиторий уже совсем не то же, что DAO. _>Отсюда и вопросы у новичков, типа, должен ли репозиторий иметь методы GetSomethingByUser, GetUserTickets или просто CRUD не зная про другие репозитории и типы.
Это шлюз таблицы, а не репозиторий. http://martinfowler.com/eaaCatalog/tableDataGateway.html
_>А современные фреймворки еще сильнее путают разрабов, реализуя весь Data Access слой. И те, что DAO называли репозиториями начинают думать, что репозитории не нужны, и можно Linq-кодом прошить всю бизнес логику. Даже картинки у Фаулера и на MSDN способствуют тому, что репозиторий воспринимается, главным образом, как провайдер данных, т.е. абстракция над RDBMS, хотя его роль более значимая при проектировании. Возможно, я начитался DDD книжек.
Фаулер отличает репозиторий и шлюз таблицы — у него это разные концепции. Проблема примеров — им надо показать всё и сразу, поэтому они очень сильно упрощают код. Код из примеров нельзя воспринимать как руководство к действию.
_>Не кажется ли вам, что пора идею репозитория несколько подправить, чтобы когда один говорил другому этот термин, то оба понимали что-то одно?
Репозиторий — это просто коллекция объектов из предметной области. Когда люди заводят речь о репозиториях, они имеют в виду совсем другое — как объекты предметной области будут существовать между запусками программы. Абсолютно две разных проблемы — управление множеством объектов и управление жизнью объектов. Надо задавать наводящие вопросы и уточнять термины.
Здравствуйте, Vladek, Вы писали: V>Вся магия наружу, эти два вызова подряд сделают две разных операции. Один вызов удалит запись, другой создаст, или наоборот.
Эй, это как бы профильный форум Тут ответы можно читать, а не пропускать и сразу переходить к "а вот почему ты неправ"
1. С кодом всё ок. Хинт: if (data == null) ... else ...
2.
Разумеется, код сферический в вакууме, просто чтоб показать основные сценарии.
Здравствуйте, Gattaka, Вы писали:
G>Здравствуйте, gandjustas, Вы писали:
G>>>И давайте от противного. А может ли ваш linq добавить этот option (recompile)? Нет не может — будет кучу запросов генерить G>>Правильно. Генерить кучу запросов выгоднее. Хотя наверное linq2db может, я не проверял. G>Выгоднее это да, но насколько? План запроса компилируется пару милисекунд, можно для каждого конкретного посмотреть в плане. И на чем мы экономим? Если сам запрос выполняется пару секунд. Ну... если хорошо скомпилировался. Если плохо — полчаса. Здесь баланс нужно соблюдать. На самом деле что ORM хорошо делает, это простейший CRUD, самый простейший. И сценарий что вы описали. Но это по факту Read.
Баланс чего? Тут нет tradeoff, запросы, сгенерированные Linq не требуют перекомпиляции, а рукопашные требуют.
G>Ну и что это за уровень абстракции в таком случае? Это всего лишь способ писать SQL на C#. Извращение...
Ну и пусть извращение, какая разница? Я готов заниматься любыми извращениями если сокращает затраты.