Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, Gattaka, Вы писали:
S>Ну у кода здорового человека оно работает из коробки. У нас для этого используется самописный стек, причём он уже существовал к моменту, когда я пришёл в команду.
То есть логика валидации на клиенте и на сервере у вас продублирована? На сервере это ОРМ с ее change tracking, а на клиенте это самописный стек, написанный к моменту когда вы пришли в команду? Окккккей...
G>Не верите. Окей, вот вам реальный бизнес сценарий. Есть компьютерная сеть в которой есть сетевые узлы и пользователи. Они имеют связи между собой, мы это все храним в базе и даем возможность через наше приложение админить. G>Таблицы User(Id, Name, Property), Network_Node(Id, Name, Property), User_User(User1Id, User2Id), Network_Node(Node1Id, Node2Id), UserOnNode(NodeId, UserId) G>В нашей базе 70000 узлов по одному пользователю на узле (для простоты). Пользователи связаны все со всеми. Для некоторый (приблизительно половины) узлов был проставлен признак Prorepry — теперь нужно добавить для таких узлов на которых есть зарегестрированные связанные пользователи . Особенно посчитайте размер таблицы User_User, это приятный момент
очень похоже на единоразовую задачу, которая решается при помощни миграции. а там пофик что да как. будет обычный SQL.
Здравствуйте, HeBpuMHeCkaTuHa, Вы писали:
G>>Не верите. Окей, вот вам реальный бизнес сценарий. Есть компьютерная сеть в которой есть сетевые узлы и пользователи. Они имеют связи между собой, мы это все храним в базе и даем возможность через наше приложение админить. G>>Таблицы User(Id, Name, Property), Network_Node(Id, Name, Property), User_User(User1Id, User2Id), Network_Node(Node1Id, Node2Id), UserOnNode(NodeId, UserId) G>>В нашей базе 70000 узлов по одному пользователю на узле (для простоты). Пользователи связаны все со всеми. Для некоторый (приблизительно половины) узлов был проставлен признак Prorepry — теперь нужно добавить для таких узлов на которых есть зарегестрированные связанные пользователи . Особенно посчитайте размер таблицы User_User, это приятный момент
HBM>очень похоже на единоразовую задачу, которая решается при помощни миграции. а там пофик что да как. будет обычный SQL.
Что за единоворазовая задача. Признак добавлятеся — удалятся, в этом суть администрирования и системы. Соответсвенно вместе с признаком и связи ходят... Это требование бизнеса.
Здравствуйте, Gattaka, Вы писали:
HBM>>кто мешает вытащить только те данные, которые нужно и не тащить лишних? G>ОРМ мешает. У меня, допустим замеплина таблица на сущность, а из этой сущности нужно пару столбцов для отчета. ОРМ всю сущность вытащит и дальше вы будете работать с теми полями что вам нужно.
В .NET (EF) можно создать анонимный тип, а котором указать нужные свойства/поля. Запрос вытащит только их.
Например, если есть в EF сущность вида
public class SomeEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string Data { get; set; }
public float Code { get; set; }
}
тогда запрос
context.SomeEntities
.Select(x => new { x.Id, x.Code })
.FirstOrDefault();
превратится в
SELECT TOP (1)
[c].[Id] AS [Id],
[c].[Code] AS [Code]
FROM [dbo].[SomeEntities] AS [c]
go
Здравствуйте, gandjustas, Вы писали:
G>Это человек следуя подходу "потом оптимизируем" написал вместо сложного запроса пару foreach циклов. Мало того, что получил select n+1, так еще и код представления ориентировался на объекты модели, каждый из которых требовал поднятия по несколько КБ текста. В итоге навигация строилась полсекунды.
Это не "предварительная оптимизация", т.к. сразу было видно что код не тянет. Да еще "а потом кэш добавим" это вообще откровенное предложение изначально ориентироваться на костыль. Так что это просто оправдание плохого кода мотивом "ну все знают про вред преждевременной оптимизации".
Предлагаю считать что это пример того, что нельзя писать фиговый код, прикрываясь тезисом о преждевременной оптимизации.
Здравствуйте, Gattaka, Вы писали:
S>>Ну у кода здорового человека оно работает из коробки. У нас для этого используется самописный стек, причём он уже существовал к моменту, когда я пришёл в команду. G>То есть логика валидации на клиенте и на сервере у вас продублирована? На сервере это ОРМ с ее change tracking, а на клиенте это самописный стек, написанный к моменту когда вы пришли в команду? Окккккей...
Ну блин, спрашивал про биндинг, делаешь вывод про валидацию
Дублирования нет — переиспользование кода никто не запрещал
Здравствуйте, Doc, Вы писали:
Doc>Здравствуйте, Gattaka, Вы писали:
Это все здорово, но только разница между таким использованием ОРМ и написанием запросов на SQL сводится к тому какой язык мы используем. Ну по факту C#, т.к. многие не знают толком SQL. Суть ОРМ ведь не в этом. Идея такая, что мы работаем с нашими доменными объектами как обычно, будто базы данных нет. Эти доменные объекты содержакт в себе данные и методы по их обработке, бизнес логику. Если нужно я эти доменные сущности не только на сервере могу использовать, но и на клиенте, да где угодно. Т.к. они не зависят от БД, от ОРМ и чего бы то ни было еще. А вот сохранение и вытаскивание объектов из БД берет на себя ОРМ и если она достаточна умная — все ок.
Здравствуйте, Gattaka, Вы писали:
G>Здравствуйте, gandjustas, Вы писали:
G>>Выкини NHibernate, возьми EF или linq2db, там нет таких проблем. G>Вы будете смеятся, но мы до этого выкинули EF. Был болезненный переход на NH... У EF тоже куча недостатков, например он не позволяется получить вам чистые доменные объекты. То есть в классах доменных сущностей у вас присутсвуют ссылки на типы из EF, а это не правильно. Я, например, должен свободно использовать доменную сущность на клиенте не потащив туда ORM... Да и запросы у EF просто жесткач некоторые, NH в этом плане получше, хотя тоже косячит.
Тут надо разобраться в истории:
Команда компилятора выпустила linq2sql чтобы оправдать наличие Expression Trees в C#. Так как парни в команде компилятора очень крутые, то и из библиотека получилась очень крутой. При этом как и весь компилятор — нифига не расширяемой. Планов на долгосрочное развитие linq2sql не было.
Параллельно упоротые чуваки из команды sql server programmability взяли никому не нужный, переусложненный маппер object spaces и натянули на него linq. Получился EF. Он толком ничего не умел, требовал наследоваться от своих классов и имел довольно странный набор возможностей.
К версии .NET 4.0 в linq2sql поправили пару багов, но больше ничего не произошло, EF тоже немного улучшили (ef4), сделали хотя бы внешние включи в сущностях, поправили немного linq провайдер. Но все равно ef4 был говном.
После этого, в 2012 году, ef отдали команде asp.net. ASP.NET команда очень крутая, решает реальные проблемы, а не выдумывает сфероконей. Первым делом они поверх дерьмового EF написали code first и миграции, с этого момента стало вообще возможно использовать EF (ef5).
Потом избавились от EDM и по максимуму выкинули тяжелое наследие object spaces (к сожалению не полностью), получился вполне достойный ORM (ef6). B самое главное выложили все исходники в опенсорс, появилось дофига поезных расширений.
Теперь решили окончательно выкинуть старый EF, оставили только code first api, для провайдера воспользовались либой relinq (которой кстати пользуется и NHibernate). Это называется EF7 или EFCore.
Куча недостатков, о которых ты пишешь — это в EF4. А если нужен маппер баз всякого барахла уже сейчас можно брать EFCore.
G>Не верите. Окей, вот вам реальный бизнес сценарий. Есть компьютерная сеть в которой есть сетевые узлы и пользователи. Они имеют связи между собой, мы это все храним в базе и даем возможность через наше приложение админить. G>Таблицы User(Id, Name, Property), Network_Node(Id, Name, Property), User_User(User1Id, User2Id), Network_Node(Node1Id, Node2Id), UserOnNode(NodeId, UserId) G>В нашей базе 70000 узлов по одному пользователю на узле (для простоты). Пользователи связаны все со всеми. Для некоторый (приблизительно половины) узлов был проставлен признак Prorepry — теперь нужно добавить для таких узлов на которых есть зарегестрированные связанные пользователи . Особенно посчитайте размер таблицы User_User, это приятный момент
И какую задачу решить надо?
G>>>>Код на SQL который более лаконичный и лучше читается не имеет никакого отношения к высокой производительности. G>>>Ну это был второй аргумент: 1. производительность, 2. локаничность и выразительность. Все таки SQL очень мощный он позволяет вам запросить что вам нужно, а не как извлеч данные. G>>Ок, напиши запрос, удовлетворяющий следующим условиям: G>>1) Если пользователь админ, то ему показать все записи G>>2) Если пользователь не админ, то G>> — показать ему записи, где пользователь = автор G>> — или автор записи входит в группу друзей текущего пользователя G>> — и записи не должны быть скрыты G>>3) Записи должны быть отфильтрованы по категории, которую выбрал пользователь (id категории — параметр запроса, может быть пусто)
G>>При этом запрос должен быть максимально лаконичным и быстрым. G>Я считаю — прекрасно, практически на естественном языке G>
G>select r.id, r.name
G>from rows as r
G>where r.IsHidden = false and
G> (exists(select * from Administrators a where a.User = r.User) or
G> exist(select * from UserFriends uf where uf.User = r.User and r.Friend = @currentUser ))
G>
1) Фильтр по категории забыл
2) Фильтр по автору поста забыл
3) Фильтр по администраторам неверный, должен быть текущий пользователь админом, а не автор
Самое главное даже в таком виде база не построит оптимальный план и запрос будет выполняться плохо.
Правильный подход — сделать 4 запроса:
1) Для админа без фильра
2) Для админа с фильтром
3) Для обычного пользователя без фильтра
4) Для обычного пользователя с фильтром
Эти 4 запрос будут работать лучше любого одного, который ты напишешь.
И с linq это приведет всего к двум if в логике программы.
Здравствуйте, Doc, Вы писали:
Doc>Здравствуйте, gandjustas, Вы писали:
G>>Это человек следуя подходу "потом оптимизируем" написал вместо сложного запроса пару foreach циклов. Мало того, что получил select n+1, так еще и код представления ориентировался на объекты модели, каждый из которых требовал поднятия по несколько КБ текста. В итоге навигация строилась полсекунды.
Doc>Это не "предварительная оптимизация", т.к. сразу было видно что код не тянет. Да еще "а потом кэш добавим" это вообще откровенное предложение изначально ориентироваться на костыль. Так что это просто оправдание плохого кода мотивом "ну все знают про вред преждевременной оптимизации".
Doc>Предлагаю считать что это пример того, что нельзя писать фиговый код, прикрываясь тезисом о преждевременной оптимизации.
Это так и есть. Но это хорошо видно в ретроспективе.
А как ты докажешь в каждом конкретном случае, что это просто фиговый код, а не попытка "преждевременной оптимизации", не зная к каким последствиям это приведет ?
Даже в этой теме высказываются мнения, что можно написать абы-какой sql, а потом "оптимизировать". Хотя в большинстве случаев "оптимизировать" нельзя, надо переписывать, причем сильно.
Здравствуйте, gandjustas, Вы писали:
G>>Не верите. Окей, вот вам реальный бизнес сценарий. Есть компьютерная сеть в которой есть сетевые узлы и пользователи. Они имеют связи между собой, мы это все храним в базе и даем возможность через наше приложение админить. G>>Таблицы User(Id, Name, Property), Network_Node(Id, Name, Property), User_User(User1Id, User2Id), Network_Node(Node1Id, Node2Id), UserOnNode(NodeId, UserId) G>>В нашей базе 70000 узлов по одному пользователю на узле (для простоты). Пользователи связаны все со всеми. Для некоторых (приблизительно половины) узлов админисстратором был проставлен признак Prorepry = true. Теперь нужно добавить связи для таких узлов (Для узлов у которых есть зарегестрированные связанные пользователи) . Особенно посчитайте размер таблицы User_User, это приятный момент G>И какую задачу решить надо?
Пофиксил описание и выделил что нужно сделать. Что скажите? Нужно еще пояснить?
Здравствуйте, Gattaka, Вы писали:
G>Здравствуйте, HeBpuMHeCkaTuHa, Вы писали:
G>>>Не верите. Окей, вот вам реальный бизнес сценарий. Есть компьютерная сеть в которой есть сетевые узлы и пользователи. Они имеют связи между собой, мы это все храним в базе и даем возможность через наше приложение админить. G>>>Таблицы User(Id, Name, Property), Network_Node(Id, Name, Property), User_User(User1Id, User2Id), Network_Node(Node1Id, Node2Id), UserOnNode(NodeId, UserId) G>>>В нашей базе 70000 узлов по одному пользователю на узле (для простоты). Пользователи связаны все со всеми. Для некоторый (приблизительно половины) узлов был проставлен признак Prorepry — теперь нужно добавить для таких узлов на которых есть зарегестрированные связанные пользователи . Особенно посчитайте размер таблицы User_User, это приятный момент
HBM>>очень похоже на единоразовую задачу, которая решается при помощни миграции. а там пофик что да как. будет обычный SQL. G>Что за единоворазовая задача. Признак добавлятеся — удалятся, в этом суть администрирования и системы. Соответсвенно вместе с признаком и связи ходят... Это требование бизнеса.
Правильно он говорит. Запрос такой будет выполнен один раз и вообще не попадет в код программы.
Ты приведи сценарий, который выполняется из программы и более одного раза.
Здравствуйте, gandjustas, Вы писали:
G>Это так и есть. Но это хорошо видно в ретроспективе. G>А как ты докажешь в каждом конкретном случае, что это просто фиговый код, а не попытка "преждевременной оптимизации", не зная к каким последствиям это приведет ?
G>Даже в этой теме высказываются мнения, что можно написать абы-какой sql, а потом "оптимизировать". Хотя в большинстве случаев "оптимизировать" нельзя, надо переписывать, причем сильно.
Никак кроме как техлид с достаточным опытом наступания на грабли...
Здравствуйте, Gattaka, Вы писали:
G>Здравствуйте, gandjustas, Вы писали:
G>>Это так и есть. Но это хорошо видно в ретроспективе. G>>А как ты докажешь в каждом конкретном случае, что это просто фиговый код, а не попытка "преждевременной оптимизации", не зная к каким последствиям это приведет ?
G>>Даже в этой теме высказываются мнения, что можно написать абы-какой sql, а потом "оптимизировать". Хотя в большинстве случаев "оптимизировать" нельзя, надо переписывать, причем сильно.
G>Никак кроме как техлид с достаточным опытом наступания на грабли...
То есть исключительно субъективное мнение?
Тогда считайте, что у меня достаточный опыт и я говорю, что SQL всегда надо делать хорошо, а не "оптимизировать потом".
Здравствуйте, Gattaka, Вы писали:
G>Здравствуйте, gandjustas, Вы писали:
G>>>Не верите. Окей, вот вам реальный бизнес сценарий. Есть компьютерная сеть в которой есть сетевые узлы и пользователи. Они имеют связи между собой, мы это все храним в базе и даем возможность через наше приложение админить. G>>>Таблицы User(Id, Name, Property), Network_Node(Id, Name, Property), User_User(User1Id, User2Id), Network_Node(Node1Id, Node2Id), UserOnNode(NodeId, UserId) G>>>В нашей базе 70000 узлов по одному пользователю на узле (для простоты). Пользователи связаны все со всеми. Для некоторых (приблизительно половины) узлов админисстратором был проставлен признак Prorepry = true. Теперь нужно добавить связи для таких узлов (Для узлов у которых есть зарегестрированные связанные пользователи) . Особенно посчитайте размер таблицы User_User, это приятный момент G>>И какую задачу решить надо?
G>Пофиксил описание и выделил что нужно сделать. Что скажите? Нужно еще пояснить?
Скажу что админ откроет консоль, напишет запрос, выполнит и забудет. Не будет ни процедуры, ни ORM.
Здравствуйте, gandjustas, Вы писали:
G>Здравствуйте, Gattaka, Вы писали:
G>>Здравствуйте, HeBpuMHeCkaTuHa, Вы писали:
G>>>>Не верите. Окей, вот вам реальный бизнес сценарий. Есть компьютерная сеть в которой есть сетевые узлы и пользователи. Они имеют связи между собой, мы это все храним в базе и даем возможность через наше приложение админить. G>>>>Таблицы User(Id, Name, Property), Network_Node(Id, Name, Property), User_User(User1Id, User2Id), Network_Node(Node1Id, Node2Id), UserOnNode(NodeId, UserId) G>>>>В нашей базе 70000 узлов по одному пользователю на узле (для простоты). Пользователи связаны все со всеми. Для некоторый (приблизительно половины) узлов был проставлен признак Prorepry — теперь нужно добавить для таких узлов на которых есть зарегестрированные связанные пользователи . Особенно посчитайте размер таблицы User_User, это приятный момент
G>Ты приведи сценарий, который выполняется из программы и более одного раза.
Нет не правильно он говорит. У вас есть софт по администрированию сети. Суть софта выставлять вот такие вот признаки на узлах. Вы как администратор их периодически выставляете и снимаете у разных узлов.
Я максимально упростил вам задачу. На самом деле это не булевый признак и т.д. и т.п.
Здравствуйте, gandjustas, Вы писали:
G>Скажу что админ откроет консоль, напишет запрос, выполнит и забудет. Не будет ни процедуры, ни ORM.
Какую консоль? Он о существовании БД не подозревает.
Здравствуйте, Gattaka, Вы писали:
G>Коллеги, G>Так ли плохо реализовывать бизнес логику в высокопроизводительных хранимых процедурах? Либо сейчас модно использовать кодогенераторы типа ОРМ, которые генерируют ужасные sql запросы? Учитывая, что код на sql как правило более локаничный и лучше читается.
Конечно можно, потому что ни хранимки, ни ORM не имеют никакого отношения к бизнес-логике. Они решают задачи хранения данных программы между её запусками.
Многие и не подозревают, что в программах, которые они пишут, нет никакой бизнес-логики. Пользователи хотят вводить данные и потом их искать, вот и вся логика. Это нормально, главное не тратить время на призрачную бизнес-логику — она там и не нужна.
Если программа без БД не имеет смысла, то это просто альтернативный интерфейс к БД.
Здравствуйте, Gattaka, Вы писали:
G>>>>>Таблицы User(Id, Name, Property), Network_Node(Id, Name, Property), User_User(User1Id, User2Id), Network_Node(Node1Id, Node2Id), UserOnNode(NodeId, UserId) G>Нет не правильно он говорит. У вас есть софт по администрированию сети. Суть софта выставлять вот такие вот признаки на узлах. Вы как администратор их периодически выставляете и снимаете у разных узлов. G>Я максимально упростил вам задачу. На самом деле это не булевый признак и т.д. и т.п.
Давай подробный сценарий с точки зрения пользователя.
Админ заходит, выбирает node, а дальше что?
Здравствуйте, Gattaka, Вы писали:
G>Это все здорово, но только разница между таким использованием ОРМ и написанием запросов на SQL сводится к тому какой язык мы используем. Ну по факту C#, т.к. многие не знают толком SQL. Суть ОРМ ведь не в этом. Идея такая, что мы работаем с нашими доменными объектами как обычно, будто базы данных нет. Эти доменные объекты содержакт в себе данные и методы по их обработке, бизнес логику. Если нужно я эти доменные сущности не только на сервере могу использовать, но и на клиенте, да где угодно. Т.к. они не зависят от БД, от ОРМ и чего бы то ни было еще.
А кто сказал что в хранилище должны храниться сущности BL именно в виде объектов BL? У нас есть DataAccess который возвращает объекты BL, а как там внутри DA — это проблемы DA.
G>А вот сохранение и вытаскивание объектов из БД берет на себя ОРМ и если она достаточна умная — все ок.
Ну вот тут не ясно. А как она должна догадаться что вы хотите только 2 поля из объекта. Вы как-то сами себя загоняете в круг "хочу чтобы ORM сама все вытаскивала — ORM плохая потому что вытаскивает все".
Здравствуйте, gandjustas, Вы писали:
G>Это так и есть. Но это хорошо видно в ретроспективе. G>А как ты докажешь в каждом конкретном случае, что это просто фиговый код, а не попытка "преждевременной оптимизации", не зная к каким последствиям это приведет ?
Только субъективный опыт. Например, меня бы смутила необходимость "поднятия по несколько КБ текста". Ну а точно задумался бы после получения 0.5 сек на создание меню и предложения добавить кэш.