Data Access Layer для простых смертных
- Условных фильтров, выполняемых на стороне БД.
- Условного выбора подмножества колонок (часть колонок тяжелые)
Не похоже на реальную работу с БД в большом сложном проекте. Нет самого главного.Мои текущий и предыдущий проекты - большие и сложные. В обоих я обошёлся без того, что ты назвал "самым главным". Потому что ты привёл весьма специфические требования, которые нужны в худшем случае для пары-тройки запросов на весь проект. Нет смысла ради этого усложнять сотни других запросов.
Опять же, хочу подчеркнуть, что простые вещи я стараюсь делать просто. Глупо вообще рассматривать какое-то решение, которое простые вещи решает уже сложно.
Мои текущий и предыдущий проекты - большие и сложные.В измеримых штуках - это сколько?
Размер базы
Кол-во записей
Кол-во запросов в секунду на пике
Кол-во функциональных точек
Кол-во сущностей в проекте
Кол-во полей у сущности (в среднем)
Кол-во таблиц в БД
Кол-во полей в таблице(в среднем)
Кол-во сущностей и полей, участвующих в одном запросе (в среднем)?
В измеримых штуках - это сколько?Батенька, вы уже куда-то не в ту степь уехали. У меня солидное резюме, но оно не имеет отношения к этому тредику.
Размер базы
Кол-во записей
Кол-во запросов в секунду на пике
Кол-во функциональных точек
Кол-во сущностей в проекте
Кол-во полей у сущности (в среднем)
Кол-во таблиц в БД
Кол-во полей в таблице(в среднем)
Кол-во сущностей и полей, участвующих в одном запросе (в среднем)?
Батенька, вы уже куда-то не в ту степь уехали.Мой тезис: приведенный пример в начальном сообщении не имеет никакого отношения к боевым проектам.
В примере ты вытягиваешь каждый раз всю таблицу в код. Это означает, что количество записей не больше 10тыс. Это не является большим и сложным проектом.
Твой "тезис" полностью соотвествует понятию "модельная задача" из первого поста.
Мой тезис: приведенный пример в начальном сообщении не имеет никакого отношения к боевым проектам.Мой тезис: пример прекрасно показывает, как работает технология. И что простые вещи можно решать просто.
В примере ты вытягиваешь каждый раз всю таблицу на клиента. Это означает, что количество записей не больше 10тыс. Это не является большим и сложным проектом.
Да, в примере кода я выполнил условие задачи. Я ведь три раза специально для таких как ты написал, что она модельная.
По-твоему, что именно я не смогу сделать в "большом и сложном проекте"?
Твой "тезис" полностью соотвествует понятию "модельная задача" из первого поста.нет. Модельная задача - это боевой проект в миниатюре. Задача "модельной задачи" показать все те способы, которые применяются на боевом проекте.
ps
Езда на трехколесном велосипеде не является модельной задачей для езды на мотоцикле.
Модельная задача предназначена для демонстрации чего-либо. Автор топика обозначил, что он хочет продемонстрировать. Если тебе мало, придумаю другую модельную задачу и продемострируй самостоятельно то, что считаешь нужным.
я делюсь тем, что мне печально. Когда я открывал этот тред, я ожидал большего. А увидел классический пример из книжки "Как научиться работать с БД из C# за 10 дней" с задачей, которая встречается только в таких книжках.
Какова цель твоих сообщений?
Когда я открывал этот тред, я ожидал большего. А увидел классический пример из книжки "Как научиться работать с БД из C# за 10 дней" с задачей, которая встречается только в таких книжках.Я так понимаю, ты можешь решить эту тривиальную задачку намного лучше? Тогда мы ждём пример твоего кода!
Цель топика показать, что общение с БД может быть реальзовано в простых конструкциях какого-то языка, а вовсе не показать исчерпывающие возможности, покрывающие все возможные случаи жизни. Таким образом я пытаюсь тебе показать, что хотя твои претензии обоснованы в общем случае, но к данному топику отношения не имеют.
Я так понимаю, ты можешь решить эту тривиальную задачку намного лучше? Тогда мы ждём пример твоего кода!Сдаюсь сразу. На трехколесном велосипеде я лучше этого не проеду.
Цель топика показать, что общение с БД может быть реальзовано в простых конструкциях какого-то языкаЯ хочу заострить внимание на том, что:
- цель этого топика: показать, что общение с БД в простых случаях может быть реализовано в простых конструкциях языка
- этот тред является логическим продолжением предыдущих тредов
- в предыдущих тредах шло обсуждение: общение с БД в сложных случаях
Сдаюсь сразу. На трехколесном велосипеде я лучше этого не проеду.То есть твоё предыдущее сообщение нужно читать так: делать нечего, зашёл поговниться? Ну ладно, слив засчитан.
в предыдущих тредах шло обсуждение: общение с БД в сложных случаяхПрежде чем решать сложные задачи, надо научиться хорошо и просто решать простые. Не знаю о каких "предыдущих тредах" ты говоришь. Если о тех которые уже лет пять постит солнцеликий бог статической типизации, то там всё всегда заканчивается тем, что простые примеры решаются невзъебенно сложно.
- зашёл узнать что-то новое
- новое не узнал, был опечален
- по заголовку предвкушал обратное, был раздосован этим несоответствием
- по заголовку предвкушал обратное, был раздосован этим несоответствиемЗаголовок тредика вполне очевидно указывает на простоту кода и бренность бытия. Видимо, ты не только не прочитал пост, не заметив, что задачка модельная; но и не прочитал его название.
что простые примеры решаются невзъебенно сложно.Он решает более сложную классическую задачу, чем у тебя в примере.
Вывести постранично список с учетом пользовательской сортировки и пользовательских фильтров.
Твой код не переносится на эту задачу.
Не знаю как в .Net, а в JavaFX ты такую работу с БД даже в гуёвую табличку не засунешь, потому что там используются observable collection с частичным обновлением.
Он решает более сложную классическую задачу, чем у тебя в примере.Я ещё раз повторю: мне по барабану, какую задачу он решает. Код решения простых случаев такой, что хочется сгореть на месте. Видимо, именно так и чувствуют себя смертные перед ликом божественного создания с абсолютными скиллами работы с кодом.
Не знаю как в .Net, а в JavaFX ты такую работу с БД даже в гуёвую табличку не засунешь, потому что там используются observable collection с частичным обновлением.Щито?
Код решения простых случаев такой, что хочется сгореть на месте.Что ты так волнуешься? Ну, не понимаешь ты код -а и какие задачи он решает. И чёрт с ним! В Mcrosoft тебя взяли и с твоим текущим подходом. Разве это не достаточный уровень подтверждения, что ты крут, а - нет?
Сложность рождает сложность. Чтобы не повторять сложные конструкции по несколько раз тебе нужен полиморфизм относительно типов объектов, извлекаемых из БД. У тебя его не видно. По идее, должно быть что-то вроде хаскелловского Data.Binary
Всё это рождает требования к инфраструктуре гораздо сложнее.Задача, которую ты описываешь, не связана и не должна быть связана (уже в терминах coupling) с Data Access Layer. И тем более с конкретной его реализацией.
Ну, не понимаешь ты код -а и какие задачи он решает.Но я же хочу, как и ты, быть причастным к божественному. Вот и прошу решение простой задачки сделать проще, чтобы смертные могли разуметь. А там глядишь через кровавые жертвоприношения и осилим понять всё сущее.
В Mcrosoft тебя взяли и с твоим текущим подходом. Разве это не достаточный уровень подтверждения, что ты крут, а - нет?Да как ты можешь такое заявлять? Сравнивать целый магазин автозапчастей с каким-то там поисковиком. Смотри, за это тебя исключат из апостолов.
с несколькими юнит тестамиГде код юнит тестов?
...
А теперь cенсация! Пост в Development с примером кода!
Где код юнит тестов?А где твой код решения задачки?
Ты кажется изобрел BLToolkit с RSDN
Ты кажется изобрел BLToolkit с RSDNПри беглом взгляде совсем не похоже. Я же написал, что это из семейства Dapper. И ещё "изобрёл" - слишком сильное слово, там на всё от силы 500 строк кода.
Я думал, намного будет… Намного лучше будет это все.
Я думал, намного будет… Намного лучше будет это все.О, а я как раз ждал последнего покемона в этом тредике. You are welcome.
Для человека, который не в теме и просто мимо проходил: чем это лучше стандартного EntityFramework, который как раз и позиционирован для простых смертных?
Для человека, который не в теме и просто мимо проходил: чем это лучше стандартного EntityFramework, который как раз и позиционирован для простых смертных?Это всё очень легко гуглится. Например: проблема генерации запросов в EF6, сравнение производительности EF7 и Dapper, ещё одно сравнение производительности, танцы с бубном для решения повседневных задач
В общем и целом EF, как и любая другая полновесная ORM, имеет ряд стандартных недостатков. Главными из них на сегодня являются производительность, отсутствие контроля над генерируемыми запросами (например, тяжелее избежать deadlock или сгенерировать приличного вида запрос и необходимость смешивания SQL и LINQ. В последнем случае вполне возможны ситуации, когда кода будет больше, чем на простой обёртке над ADO.NET Для сравнения можешь написать MERGE INTO из моей задачки на EF. (Вполне возможно, что в EF7 это вдруг стало просто.)
С другой стороны EF много лет развивается и совершенно ужасные вещи оттуда постепенно исчезают. Я пристально не следил за его развитием и использовал его только в тех случаях, когда у меня были разовые проекты, которые можно написать и забыть. Думаю, что для более полного сравнения надо оценивать EF7, ожидания от которого очень и очень высокие. Впрочем, как и ожидания от всех других версий EF.
отсутствие контроля над генерируемыми запросами
Внезапно - controlable query!
Внезапно - controlable query!Внезапно ты прав! Контроль над SQL толкается как основная фишка ко-ку.
чем это лучше стандартного EntityFramework, который как раз и позиционирован для простых смертных?В варианте -а, есть сахар для table-value parameters. Остальное очень похоже на System.Data.Linq или EF.
DTO приходят с клиента, через многочисленные RPC вызовы - создать отдельно кошечек, удалить кошечек, обновить кошечек, создать кошечек вместе с историями, добавить связь кошечек с историей, удалить связь кошечки с историей, тысячи их.
На клиенте можно продолжать безумие - создать описания DTO на typescript, или разработать клиентский ОРМ, который будет обращаться к серверному ОРМу.
Реально, все что нужно - декларативное описание схемы с декларативным описанием прав доступа, из которого сразу бы следовал протокол синхронизации.
Простейшая задача - две связанные таблички, которые надо редактировать.Здесь решена другая задача.
Кастомный код для вставки в эти две таблицы тоже не нужен, по большому счету - он будет одинаковым для любой пары связанных таблиц, отличаться будут только названия этих таблиц.Какой код для вставки в две таблицы ты имеешь в виду?
Реально, все что нужно - декларативное описание схемы с декларативным описанием прав доступа, из которого сразу бы следовал протокол синхронизации.Реально всё, что нужно - это быстро и качественно решать задачи. Не знаю, что именно ты понимаешь под декларативностью, но во что выливается генерация SQL запросов можешь найти выше в моём посте про EF.
в EF не пахнет особой декларативностью. Там enterpise-ность.
в EF не пахнет особой декларативностью. Там enterpise-ность.Чтобы не сводить всё к определению того, что такое декларативность, а это на 153% закончится холирваром, лучше просто увидеть, какой, скажем так, "текст" имеет в виду вместо императивного кода. Для решения всё той же простой задачки.
Не знаю, что именно ты понимаешь под декларативностью, но во что выливается генерация SQL запросов можешь найти выше в моём посте про EF.EF и всякие ОРМ - это когда отказываются от высокоуровневой (реляционной) модели данных в пользу низкоуровневой (навигационной для того чтобы программисты на IDE вызывали методы автокомплитом "через точечку". В результате, предсказуемо возникают проблемы, вызванные циклами и дублированием информации в структурах данных.
Разумеется, я против этого убожества.
Реально всё, что нужно - это быстро и качественно решать задачи. Не знаю, что именно ты понимаешь под декларативностью, но во что выливается генерация SQL запросов можешь найти выше в моём посте про EF.Я вижу ты обновляешь поле versionId. В реальной программе надо думать о concurrency и делать compare and swap по полю versionId. Надо получать от клиента список записей, делать CAS по каждой записи отдельно, и возвращать клиенту обратно список записей, которые он засабмитил, причем для записей, у которых не прошел CAS, выдавать две версии - ту которую засабмитил клиент, и ту которую засабмитил другой пользователь (чтобы пользователь мог их смерджить). Эти две версии должны выдаваться в стандартном формате (такого формата не существует кстати, надо самому придумывать).
Решать эту задачу каким-то императивным adhoc-кодом - безумие, в результате о concurrency никто не думает, придумывая отговорки типа "у нас вебскейл опердень, подумаешь какие-то данные проебутся".
Декларативное решение должно как минимум разруливать автоматически описанную ситуацию.
Я вижу ты обновляешь поле versionId.У меня в решении задачки нет поля versionId.
В реальной программе надо думать о concurrency и делать compare and swap по полю versionId.Где-то отсюда я вообще перестал понимать, что ты хотел сказать.
У меня в решении задачки нет поля versionId.Есть поле ver, я так понял что это номер версии.
Где-то отсюда я вообще перестал понимать, что ты хотел сказать.Ну, CAS - простейший concurrency примитив, решает например проблему lost update.
Есть поле ver, я так понял что это номер версии.Да, поле Ver есть и оно обновляется в базе данных.
Ну, CAS - простейший concurrency примитив, решает например проблему lost update.Я знаю, что такое CAS, но не понимаю, зачем он нужен для решения этой задачи. Здесь immutable DTO. Если же ты говоришь про работу с БД или архитектуру какой-то системы, то тогда я не понимаю, при чём тут код обращения к БД. Решение задачи, когда два пользователя обновили один и тот же объект, не зависит от технологии выполнения запросов. Кроме этого совершенно не ясно, как декларативное решение разрулит эту проблему автоматически. Это, конечно, можно запилить в виде какого-то адского DSL или фреймворка, но скорее всего получится нечто непригодное для эффективной, быстрой и простой разработки.
Позыв "давайте декларативно генерить опердени" имеет хоть одну историю успеха?
Решение задачи, когда два пользователя обновили один и тот же объект, не зависит от технологии выполнения запросов.Как ты предлагаешь решать это в рамках твоего подхода? Мне кажется, тебе придется на каждую сущность писать бойлерплейт с проверкой номера версии.
Все что тут нужно - описание табличек, все остальное - выдуманная сложность. DTO с геттерами и сеттерами являются копипастой описания таблиц.В большинстве систем это генерируется по базе. Зачем Майк это всё руками пишет не ясно.
А в настоящих оперднях типа 1c или Axapta база встроена на уровне языка/среды, там нет разделения на базу и остальное, т.е. и генерировать незачем.
Для того, чтобы избегать кодогенерации в F# запилили Type Providers.
Как ты предлагаешь решать это в рамках твоего подхода? Мне кажется, тебе придется на каждую сущность писать бойлерплейт с проверкой номера версии.Вообще не придётся. Эта задача хорошо ложится на повторное использование кода. А вот посмотреть на твои декларативные выкладки было бы интересно. Или мы сейчас обсужаем некоторую идеальную технологию, которой пока что не существует в природе?
SELECT * FROM CatЕсть ли какая-либо
Есть ли какая-либо магия обработка sql-строки?Есть и обработка строки, и обработка параметров.
Если добавить фото котиков ALTER TABLE Cat ADD Photo IMAGE. Будут ли грузиться картинки в память?В этом конкретном примере будут. Но если тебе нужно работать с дебилами, которые могут сделать такой ALTER TABLE, или ты сам из таких, то в запросе лучше написать "SELECT @CatDto".
@CatDtoЧто такое CatDto? Имя типа? Из какого неймспейса? Есть ли документация с описанием фич твоего ORM?
В код посмотри.
В код посмотри.В какой? Куда смотреть?
В первом посте.
Там нет ответа на мои вопросы.
Ты там тип не увидел?
Ты вопросы мои не увидел?
Что такое CatDto? Имя типа? Из какого неймспейса?Что такое CatDto понятно по коду.
Есть ли документация с описанием фич твоего ORM?Конечно. У меня детальные javadocs в исходном коде с описанием всех фич.
Что такое CatDto понятно по коду.Если я к твоему коду добавлю:
namespace MyNamespace2
{
class CatDto
{
string P1 { get; private set; }
string P2 { get; private set; }
}
}
Что сгенерируется в результате "SELECT @CatDto"?
Или там можно использовать только имя переданного дженерика?
Что сгенерируется в результате "SELECT @CatDto"?То же самое.
Или там можно использовать только имя переданного дженерика?Не только.
То же самое.Какой алгоритм поиска типа по имени?
Не только.
Почему не будет "SELECT P1, P2"?
Какой алгоритм поиска типа по имени? Почему не будет "SELECT P1, P2"?А какая тебе, собственно говоря, разница? Не важно, что там умеет или не умеет этот фреймворк, я его рекламировать не собираюсь. Важно, что простая задача решена простым методом, а в этом и был смысл поста.
А какая тебе, собственно говоря, разница? Не важно, что там умеет или не умеет этот фреймворк, я его рекламировать не собираюсь. Важно, что простая задача решена простым методом, а в этом и был смысл поста.Эта простая задача отлично решается на ванильном ADO.NET, который все знают.
А с помощью controlable query она решается?
Эта простая задача отлично решается на ванильном ADO.NET, который все знают.Поясни для простых смертных: ругая CQ майк сел в лужу и расхваливает свое поделие, даже кода которого показать не может?
Эта простая задача отлично решается на ванильном ADO.NET, который все знают.Так напиши это на ко-ку и давай сравним решения на "отличность". Ведь ко-ку - это же ванильный ADO.NET, не так ли?
Поясни для простых смертных: ругая CQ майк сел в лужу и расхваливает свое поделие, даже кода которого показать не может?Да, Майк сел в лужу поскольку не выложил полностью код.
Не выложил:
юнит тесты, которые противопоставляет CQ;
нет кода ORM;
нет DDL скрипта базы.
Если следовать твоей логике, то ты в луже просто утонул, ибо не выложил ничего вообще.
Да, Майк сел в лужу поскольку не выложил полностью код.Реально? Я написал код, выложил его без всяких требований и дал столько великолепных возможностей меня потроллить. Блин, да когда я думаю обо всех этих возможностях мне хочется потроллить самого себя!
Но всё, на что ты оказался способен - это жалко промямлить какие-то несвязные претензии в ответ на кукареканье петушка _no_? Как школьница, которую вызвали к доске, и которая покраснела, потому что не сделала домашнее задание.
Скажи, это с тобой сделали ~100 веток и несколько тысяч коммитов в ко-ку; или многолетняя и тяжёлая работа в магазине автозапчастей?
Если следовать твоей логике, то ты в луже просто утонул, ибо не выложил ничего вообще.Тебе не хватает знаний в осуждаемой теме. Разжевывать лично для тебя детали моей логики у меня нет никакого резона.
Вообще не придётся. Эта задача хорошо ложится на повторное использование кода. А вот посмотреть на твои декларативные выкладки было бы интересно. Или мы сейчас обсужаем некоторую идеальную технологию, которой пока что не существует в природе?TLDR: я хочу напрямую работать с базой данных с клиента, чтобы можно было быстро прототипировать и потом мягко переходить с прототипа на решение продакшен уровня.
На чтение мне должны быть с клиента доступна вся схема (там могут быть какие-то вычисляемые или виртуальные поля, за которыми стоит произвольный код). Например, фейсбук позволяет делать FQL (типа SQL) запросы к данным, там можно даже какие-то джоины делать. Я думаю, что такой подход правильный, ему должны следовать все.
На запись база тоже должа быть открыта (это ограниченный подход, но позволяет быстро прототипировать).
Нужен стандартный формат синхронизации реляционных данных. Пейлоадом должен быть набор записей типа "добавился такой-то тапл, удалился такой-то, а у третьего обновились такие-то поля". Данные могут как отправляться пакетом, так и непрерывно стримиться по вебсокетам. В респонсе должен отдаваться для каждого тапла статус, удается ли его сохранить, или если не удается, то почему не удалось - ошибка валидации, если конфликт concurrency, то выдать запись, с какой произошел конфликт.
Это должен быть стандартный формат, чтобы не надо было велосипедить.
Кроме того, должна быть имплементация такого сервера. В простейшем случае он должен просто смотреть в базу данных, чтобы можно было быстро прототипировать. Для более сложных случаев нужна имплементация row level security, для еще более сложных - возможность выполнять кастомный код (sort of triggers). В реально сложных случаях работаем с базой уже через слой процедур (как это сейчас делается).
должныТы написал это слово 8 раз. Беда в том, что никто ничего никому не должен. Я хочу посмотреть на реальный пример декларативного подхода, решающего задачку в тредике, а не перечисление требований к какой-то системе. Если такой технологии нет, или она "должна быть", но её всё равно нет, то нет и предмета обсуждения.
Так напиши это на ко-ку и давай сравним решения на "отличность".Если, действительно, хочешь сравнивать, тогда нужно выписать критерии, по которым будем сравнивать.
Если, действительно, хочешь сравнивать, тогда нужно выписать критерии, по которым будем сравнивать.В твоём тредике я не выдвигал никаких критериев и не собирался. Надо было просто решить эту задачку и всё.
Выбор критериев вполне растянется на сотни сообщений. И вообще как-то странно сравнивать два решения, одно из которых написано за 15 минут в студии в режиме блокнота, а второе будет тщательно подгоняться под критерии. Не волнуйся, петушок _no_ тебя похвалит в любом случае.
Лучи РАДОСТИ тебе Майк!
У тебя тоже будет возможность выложить обновленную версию под критерии.
У тебя тоже будет возможность выложить обновленную версию под критерии.Это у тебя есть возможность написать код без критериев, как ты любишь.
Есть и обработка строки, и обработка параметров.Для этого используется Razor?
Это у тебя есть возможность написать код без критериев, как ты любишь.Ну глумитесь что ли. Писал в Sublime.
var stories = new {}.Apply(p => new SqlCommand("SELECT * FROM Cat").Query<Cat>.Join(
new {}.Apply(p => new SqlCommand("SELECT * FROM Story").Query<Story>.Where(VeryComplexFilter
cat => cat.Name, story => story.Name,
(cat, story) => new CatStory(cat.Id, VeryComplexPreview(cat, story cat.Age + story.Age;
MakeTransaction(IsolationLevel.RepeatableRead, transaction => {
foreach (var partition in stories.Partition(300
MergeCatStoryQuery(partition).Execute(transaction);
foreach (var partition in stories.Select(_ => _.Id).Partition(1000
UpdateCatQuery(partition).Execute(transaction);
});
static Query<Non> MergeCatStoryQuery(IEnumerable<CatStory> stories)
{
var command = new SqlCommand;
command.CommandText = new StringBuilder(@"
MERGE INTO CatStory
USING VALUES(").AppendTableValue(stories.Select(_ => new {_.Id, _.Preview, _.Source} command)
.Append(@") AS Source(Id, Preview, Age)
ON PreviewTable.Id = Source.Id
WHEN NOT MATCHED BY TARGET THEN
INSERT VALUES(Source.Id, Source.Preview, Source.Age)
WHEN MATCHED THEN
UPDATE SET Preview = Source.Preview, Age = Source.Age").ToString;
return command.Query<Non>
}
static Query<Non> UpdateCatQuery(IEnumerable<int> ids)
{
var command = new SqlCommand;
command.CommandText = new StringBuilder("UPDATE Cat SET Ver = Ver + 1 WHERE Id IN (")
.AppendList("@id", ids, command).Append(")").ToString;
return command.Query<Non>
}
bool VeryComplexFilter(Story story)
{
return story.Text.Contains("something");
}
string VeryComplexPreview(Cat cat, Story story)
{
return story.Text.Substring(100);
}
class CatStory
{
public int Id { get; }
public string Preview { get; }
public int Age { get; }
public CatStoryDto(int id, string preview, int age)
{
Id = id;
Preview = preview;
Age = age;
}
}
Ну глумитесь что ли. Писал в Sublime.Глумиться рано. В пункте 1 моей задачи написано: "Названия полей в типах данных могут не совпадать с названиями столбцов." В моём примере видно, что Join выполнен по свойству Name, которое в таблице называется Alias. В твоём решении этот пункт не выполнен?
"Названия полей в типах данных могут не совпадать с названиями столбцов."Не вижу смысла давать стопицот имен одному и тому же. Только путаться.
Не вижу смысла давать стопицот имен одному и тому же. Только путаться.Не важно, видишь ты смысл или нет. Это пункт условия задачи, который нужно выполнить.
Не важно, видишь ты смысл или нет. Это пункт условия задачи, который нужно выполнить.Ты эту задачу позиционировал как близкую к реальности. Откуда такое требование в реальности?
Ты эту задачу позиционировал как близкую к реальности. Откуда такое требование в реальности?Сначала позиционировал. Но оказалось, что она всего лишь из книжки "как научиться за 10 дней". Теперь вот оказывается, что одно из простейших требований так сложно выполнить, что про него написано уже 4 поста. Но я буду снисходителен и приведу в пример твою же цитату:
А в настоящих оперднях типа 1c или Axapta база встроена на уровне языка/среды, там нет разделения на базу и остальное, т.е. и генерировать незачем.Кто сталикивался с интеграцией околоодинэса, тот знает, что в реальности моё требование вполне себе обосновано. Так что пока что у тебя есть какой-то код, но в нём нет решения моей задачки из книжки "как научиться за 10 дней".
Для общения с внешней системой я бы использовал отдельные механизмы, к Data Access Layer не имеющие отношения. Там отличий в структуре данных будет больше, чем переименование полей.
Для общения с внешней системой я бы использовал отдельные механизмы, к Data Access Layer не имеющие отношения. Там отличий в структуре данных будет больше, чем переименование полей.Ну вот и напиши в своём коде эти механизмы. У тебя есть всё тот же один connection string.
Как же я напишу, если я о внешней системе ничего не знаю.
Как же я напишу, если я о внешней системе ничего не знаю.Ты знаешь, что название столбца не совпадает с названием поля класса. Вот этот пункт надо выполнить.
Как во внешнюю систему выгружаются .NET классы?
Как во внешнюю систему выгружаются .NET классы?Щито? Я уже перестаю тебя понимать. Просто выполни требование задачки или скажи, что не можешь.
USING VALUES(").AppendTableValue(stories.Select(_ => new {_.Id, _.Preview, _.Source} command)Рассматривался ли следующий вариант?
.Append(@") AS Source(Id, Preview, Age)
... = Command("... USING VALUES({0}) ...", stories.Select(_ => new {_.Id, _.Preview, _.Source};
Какие аргументы склонили чашу весов в пользу используемого варианта?
Щито? Я уже перестаю тебя понимать. Просто выполни требование задачки или скажи, что не можешь.Мой код делает ровно то же, что и твой. А как называть переменные это уже мое дело. Пожалуйста, расширяй функциональные требования в своей задачке. Сейчас все функциональные требования мой код выполнил.
Рассматривался ли следующий вариант?Имхо, общности не хватает, синтаксис для частного случае. У меня ванильные SqlCommand и StringBuilder с добавлением простого метода расширения AppendTableValue.
code:
... = Command("... USING VALUES({0}) ...", stories.Select(_ => new {_.Id, _.Preview, _.Source};
Мой код делает ровно то же, что и твой.Нет, не делает. Не выполнено требование, что столблцы имеют другое название. Не надо втирать, что требование неправильное. Оно как раз из книжки "как научиться за 10 дней", там ничего сложного не пишут. Ты можешь отказаться, только если не можешь его выполнить.
Razor используется в твоем ORM?
Razor используется в твоем ORM?Это как-то влияет на то, что ты не можешь решить простейшую задачку?
Просто ответь используется или нет?
Просто ответь используется или нет?Не хочу. Я прекрасно знаю, что вопросы можно задавать до бесконечности. Считаю, что все ответы есть в первом посте в виде кода. Как написать такую библиотеку можно узнать у , а то я позабыл автора и название книжки.
Вернёмся к теме твоего кода. Реализуй первый пункт задачи. Я почему-то думаю, что в твоём коде нужны совсем небольшие изменения. Впрочем, ты можешь просто сказать, что не можешь этого сделать.
Как написать такую библиотеку можно узнать у , а то я позабыл автора и название книжки.2 что за книжка?
Теперь ты ответь, используется или нет Razor в твоем ORM?
2 что за книжка?троллит. Не может забыть и простить, что предложенную им задачу я назвал модельной из книжки. )
ps
По парсерам фундаментальные книги:
- Ахо. Компиляторы
- Dick Grune. Parsing Techniques. A Practical Guide
Естественно, я могу реализовать такое простое требование, есть множество вариантов.Ну вот выбери и реализуй. А пока что твой код вообще не работает.
Ну вот выбери и реализуй. А пока что твой код вообще не работает.Зачем мне реализовывать то, что я считаю лишним? Какая у меня мотивация? Код работает. Кто потребитель этих лишних требований? За это кто-то мне заплатит? Кто?
Зачем мне реализовывать то, что я считаю лишним?За тем, что ты залез в мой тредик и приводишь решение моей задачки. Какой смысл начинать рассматривать твой код, если ты не осилил решить поставленную задачку.
Но если ты настаиваешь, то OK. [Сравнение.] Мой код решает поставленную задачу, твой код не решает. [Конец сравнения.]
Посмотри исходники Dapper-а. Там схожий синтаксис и скорее всего есть ответ на твой вопрос. Предполагаю, что брал его за основу, а не писал полностью с нуля. Он не похож на любителя велосипедов.
Посмотри исходники Dapper-а. Там схожий синтаксис и скорее всего есть ответ на твой вопрос.В Dapper наколеночный вариант на регекспах. Например, вот такой код дает неправильный результат.
var row = connection.Query<string>(
"SELECT 'test @@Ids' as c1 WHERE 1 IN @Ids",
new { Ids = new[] { 1, 2, 3 } }).ToList;
Предполагаю, что брал его за основу, а не писал полностью с нуля. Он не похож на любителя велосипедов.
У Майка есть синтаксис для table value constructor и @CatDto, в Dapper таких фич нет.
Майк подробно , как я встроил Razor в CQ. , что в моем репозитарии куча веток. Спрыгнул с темы: "", когда я начал расспрашивать подробности как обрабатывается строка. Четыре раза не ответил на прямой вопрос, используется ли Razor.
Как правильно заметил No, Майк не показывает код своего ORM.
Всё это наводит на подозрение, что Майк украл идею и реализацию из CQ и боится это открыть.
Всё это наводит на подозрение, что Майк украл идею и реализацию из CQ и боится это открыть.Ты не боишься моего ответа, который последует за этим обвинением?
Если нет открой код с алгоритмом как обрабатывается строка. Простые смертные должны знать как обрабатывается строка, которую они пишут.
Простые смертные должны знать как обрабатывается строка, которую они пишут.Простые смертные никому ничего не должны. А ты у нас вообще бог всея статической типизации, так что тебе об этом знать не положено.
Хватит уводить мой тредик куда-то в сторону. Пока что ты не осилил решение задачки, вот на этом и надо заострять внимание.
Ты много лет поливал грязью CQ. Предлагал его автору суицид. И внезапно возникают большие подозрения, что ты воспользовался частью этого проекта с открытым кодом. На этом надо заострять внимание, это гораздо интересней, чем обсуждать названия колонок.
У Майка есть синтаксис для table value constructor и @CatDto, в Dapper таких фич нет.Table-value параметры и маппинг на поля - это семантика, а не синтаксис. На разборе выражения эти фичи не участвуют и не меняют процесс разбора выражения, они участвуют на этапе генерации конечного запроса на основе разобранного выражения и данных.
Ты много лет поливал грязью CQ. Предлагал его автору суицид. И внезапно возникают большие подозрения, что ты воспользовался частью этого проекта с открытым кодом. На этом надо заострять внимание, это гораздо интересней, чем обсуждать названия колонок.Хорошо. Такие серьёзные обвинения мы обсудим в новом тредике.
Он не похож на любителя велосипедов.А я вроде как раз за велосипеды его и гнобил несколько лет назад
А я вроде как раз за велосипеды его и гнобил несколько лет назадТы гнобил? Звучит смешно, но ты ведь и правда в это веришь?
Ты гнобил? Звучит смешно, но ты ведь и правда в это веришь?Главное что ты веришь, баттхертишь до сих пор
Вполне предполагаю, что у есть свои годные велосипеды. Но это будут сермяжистые велосипеды, решающие практичные задачи. В душе -а нет места для "еще одного парсера" мета-sql-ных выражений. Нет истинного размаха русской души - бессмысленного и беспощадного. )
В душе -а нет места для "еще одного парсера" мета-sql-ных выражений. Нет истинного размаха русской души - бессмысленного и беспощадного. )Хмм... Это что за новый вид троллинга? *собака-вопросяка.жпг*
Вполне предполагаю, что у есть свои годные велосипеды. Но это будут сермяжистые велосипеды, решающие практичные задачи.Вшмышле в его любимом ентерпрайзном говне нет нормального ORM, поэтому он вынужден выдумывать свой?
кстати, IQueryable вообще не используешь при работе с БД? в своей библиотеке?
Вшмышле в его любимом ентерпрайзном говне нет нормального ORM, поэтому он вынужден выдумывать свой?Спокойней, не проецируй свой неудачный опыт с питоном на всех людей и все технологии. А то вдруг ещё крыша протечёт. Судя по твоему поведению, она у тебя уже капает.
Если б капала у меня - я бы писал гандонистые посты про знакомых мне форумчан, которых собеседовал или с которыми знаком по работе.
Если б капала у меня - я бы писал гандонистые посты про знакомых мне форумчан, которых собеседовал или с которыми знаком по работе.Ты хочешь сказать, что она у тебя уже окончательно протекла? Ведь основное твоё времяпровождение на форуме - это появляться в любых темах и пытаться писать там гадости не зависимо от того, знаком ты с кем-то или нет. Конечно, по большей части у тебя не получается писать гадости, зато глупостей ты пишешь хоть отбавляй.
Вшмышле в его любимом ентерпрайзном говне нет нормального ORM, поэтому он вынужден выдумывать свой?Штатные устаревают быстро. Крупные open source-ные тоже. И тем, и другим приходится много усилий тратить на обратную совместимость. Последние фичи sql-сервера, .net-а и c# появляются с сильным запаздыванием.
Соответственно, допиливание существующего проекта под себя - вполне выгодный и практичный подход. У -а вижу допиленные table-value-parameters и bulk. Первое появилось недавно, второе вспомогательная фишка. И то, и другое - ускоряет производительность. Это не особо важно на enterprise-ных проектах (им проще заливать проблемы железом но полезно на частных проектах.
кстати, IQueryable вообще не используешь при работе с БД? в своей библиотеке?В смысле составления запросов по LINQ Expressions? Не использую. Я давно забил на то, чтобы сразу делать cross db проекты, предпочитаю использовать db specific синтаксис и технологии. Для этого проще писать запросы руками - они получаются более короткими и их проще прочитать. Ценность статической типизации считаю завышенной.
Ценность статической типизации считаю завышенной.Да, в IQueryable присутствует статическая типизация. Вторая фишка IQueryable-а - декомпозиция. Появляется возможность отделить друг от друга: маппинг таблицы на класс, фильтрацию, paging, сортировку и т.д.
Как у тебя получается обходится без декомпозиции в sql-запросах? Или ты её как-то по другому делаешь?
Как у тебя получается обходится без декомпозиции в sql-запросах?Без какой декомпозиции?
Если б капала у меня - я бы писал гандонистые посты про знакомых мне форумчан, которых собеседовал или с которыми знаком по работе.Причем он дал слово не делать этого
Как у тебя получается обходится без декомпозиции в sql-запросах? Или ты её как-то по другому делаешь?Он с 2010 года пилил в Связном один единственный проект, который сделал на антипатерне для реляционки key-value.
Теперь полгода работает в Бинге, грузит всё в память, как ты сам видел. Он не понимает проблематики. Не сталкивался.
Без какой декомпозиции?в одном месте - необходимы котики в возрасте до года, во втором - с alias-ом pretty, в третьем - version = 1
на IQueryable:
context.Cats
.Where(cat => cat.Age < 1)
context.Cats
.Where(cat => cat.Alias == "pretty")
context.Cats
.Where(cat => cat.Version == 1)
Эти запросы есть возможность передать в единую процедуру по страничной обработки:
void PageProcessing(IQueryable<CatDto> items)
{
var query = items.OrderBy(item => item.Id);
for (var i = 0;;i += 1000)
{
var currentItems = query.Skip(i).Take(1000);
if (!currentItems.Any
break;
..
}
}
кстати, ты что используешь для доступа к бд на питоне?
Эти запросы есть возможность передать в единую процедуру по страничной обработки:Согласен. Но тут надо понять, к чему сводится твой пример или вопрос: к динамическим запросам или к повторному использованию кода с фиксированными условиями. В последнем случае часто встречается ситуация, когда лучше написать три db specific запроса для разных случаев.
public static Func<int, IQueryable<Cat>> Page(this IQueryable<Cat> items)
{
return i => items.OrderBy(item => item.Id).Skip(i).Take(pageSize);
}
public static void PageProcessing(Func<int, IQueryable<Cat>> items)
{
for (var i = 0;; i += pageSize)
{
var currentItems = items(i);
if (!currentItems.Any
break;
....
}
}
const int pageSize = 1000;
Тогда запросы:
1. Cats.Where(cat => cat.Age < 1).Page;
2. Cats.Where(cat => cat.Alias == "pretty").Page;
3. Cats.Where(cat => cat.Version == 1).Page;
можно покрывать unit тестами на DAL, не касаясь функции PageProcessing.
А как ты покроешь unit тестами DAL? PageProcessing в DAL не входит. Фактически твоя декомпозиция смешивает DAL с другими слоями. Я не говорю что это плохо. Исчезают границы DAL-а, поэтому unit тесты на DAL уже не напишешь.
Есть олдскульный подход: выделять DAL и покрывать его тестами.
Oracle на своем сайте в учебнике до сих пор продвигает еще более олдскульный вариант: весь DAL делать в базе.
CQ напротив сужает границы классического DAL, получается некий Query Layer.
Для покрытия тестами слоя Query Layer требуется значительно меньше усилий, чем для классического DAL.
Если DAL полностью покрывать тестами, то смысла в IQueryable становится совсем немного. Именно на это и нацелен IQueryable, который позиционируют, как технологию, которая избавляет от написания рутинных тестов на DAL. Поэтому мой первый вопрос в этом треде был: покажи код юнит тестов.
Data Access Layer для простых смертных
Забавно читать историю:
:
Я ответил на все твои вопросы. Теперь твоя очередь. Лучше всего, если будет код.
:
Моей очереди не будет. Если ты ещё не понял, то я не занимаюсь созданием какого-то фреймворка для работы с БД.
2012 год .... и .... в 2015 Майк выкладывает код на своем ORM. А я, да, не понимаю.
Я давно забил на то, чтобы сразу делать cross db проекты, предпочитаю использовать db specific синтаксис и технологии.В 2013 году Майк обвинял CQ в db specific:
Ты не понял. В Controllable Query "автоматическое тестирование" - это SQL Server Only либа, которая ищет все запросы, строит их планы, парсит эти планы и проверяет типы данных.
Не забывай, что общаешься с .NET SQL Server Only типчиком.
Растет мальчик. Через лет 5 начнет осознавать проблему, которую решает CQ.
А как ты покроешь unit тестами DAL? PageProcessing в DAL не входит.Используя модифицированный твой подход.
Допустим у нас есть IQueryable-функция:
IQueryable<Cat> Kittens
{
return context.Cats
.Where(cat => cat.Age < 1);
}
Для этой функции необходимо протестировать следующие варианты на то, что они генерят правильный sql:
- непосредственно её
- Kittens.Skip(x)
- Kittens.Take(x)
- Kittens.OrderBy(cat => cat.<field>) (для всех полей Cat)
- комбинации ранее перечисленного
Query<ICat>.New(new {}, "@Kittens");
Query<ICat>.New(new {}, "SELECT * FROM (@Kittens) _ ORDER BY Id");
Query<ICat>.New(new {x = 10}, "SELECT TOP (@x) * FROM (@Kittens) _");
Query<ICat>.New(new {x = 10, y = 20}, @"
SELECT *
FROM (@Kittens) _
ORDER BY Age OFFSET @x ROWS FETCH NEXT @y ROWS ONLY");
internal static QFragment Kittens
{
get { return new {}.QFragment("SELECT * FROM Cat WHERE Age < 1"); }
}
Кстати, гугл по запросу "data access layer testing" выдает первой строчкой: . О ужас, они сказали это! Тесты юнит — это мусор.
Тестировать надо не саму функцию, а код, который ее вызывает:Тестировать стоит и то, и другое.
Функция Kittens тестируется на то, что без ошибок получает данные из БД
Функция PageProcessing тестируется на то, что она без ошибок обрабатывает данные постранично.
Тестировать стоит и то, и другое.Если функция нигде не вызывается, ее тестировать не надо. А лучше ее вообще удалить. Если она вызывается, то см. мой предыдущий пост.
Функция Kittens тестируется на то, что без ошибок получает данные из БД
Это переход к теме Unit testing vs Integration testing.
До этого речь шла о тестирование DAL, что делается в рамках Unit Testing.
ps
В рамках Unit testing, логику вышестоящей функции имеет смысл тестировать без привязки к DAL на inmemory-данных.
В рамках Unit testing, логику вышестоящей функции имеет смысл тестировать без привязки к DAL на inmemory-данных.И в чем смысл?
И в чем смысл?В том чтобы протестировать логику, которая находится непосредственно в этой функции.
Допустим вышестоящая функция следующая:
void ProcessByEvent(string event)
{
var sources = new Dictionary<string, Func<IQueryable<Cat>>>
{
{"Kitten", Kittens},
{"Pretty", PrettyCats},
{"New", NewCats},
};
var source = sources.Find(event);
if(source != null)
PageProcessing(source);
}
В рамках Unit Testing эту функцию не имеет смысла тестировать на то, что она корректно получает данные из БД.
Имеет смысл для этой функции зафиксировать пару тестов на то, что она вызывает PageProcessing с правильным источником для event-а.
Странный пример, если я его правильно понял, то непосредственно вызов базы происходит только внутри PageProcessing. Естественно роутер, который ничего про БД не знает, можно тестировать без БД.
Естественно роутерВ чем тогда вопрос?
ps
Kittens - тестируем на то, что она корректно генерирует запросы к БД (по сценарию приведенному выше в треде)
PageProcessing - тестируем на одном варианте IQueryable<Cat> на то, что она корректно обрабатывает данные постранично
ProcessByEvent - тестируем на то, что она передает корректный source в PageProcessing
В этом кейсе, хочется или требуется протестировать что-то еще?
В рамках Unit testing, логику вышестоящей функции имеет смысл тестировать без привязки к DAL на inmemory-данных.Интересно тут тестирование PageProcessing. Мое мнение состоит в том, что ее лучше тестировать с настоящей базой, а не inmemory-моками.
П.с.
зафиксировать пару тестов на то, что она вызывает PageProcessing с правильным источником для event-а.Как я ненавижу такие тесты! Сколько не видел подобных, ни разу они не помогали найти регрессию, а вот боли при рефакторинге доставляют немеренно.
Имеет смысл для этой функции зафиксировать пару тестов на то, что она вызывает PageProcessing с правильным источником для event-а.Как будешь проверять правильность источника? И чтобы не плодить посты напиши код тестов. А то о тестах все говорят, но никто их не приводят. Если приводят, то без слез на это не взглянешь: одна табличка из пяти полей и пять сотен строк тестов http://github.com/pkainulainen/jooq-with-spring-examples/bl...
Как я ненавижу такие тесты! Сколько не видел подобных, ни разу они не помогали найти регрессию, а вот боли при рефакторинге доставляют немеренно.А какие тесты отлавливают регрессию и не особо причиняют боль при внесении изменений?
А то о тестах все говорят, но никто их не приводят. Если приводят, то без слез на это не взглянешь: одна табличка из пяти полей и пять сотен строк тестов http://github.com/pkainulainen/jooq-with-spring-examples/bl...Ну это же джава, там нормально считается скопипастить 20 строк кода 5 раз подряд.
Ну это же джава, там нормально считается скопипастить 20 строк кода 5 раз подряд.Java да, доставляет. Но тут не только в ней дело. Вот тут чувак пишет, что писали юнит тесты на DAL, а потом понял, что это ненужный мусор . Теперь начал писать интеграционные тесты. Судя по примерам кода, это тесты типа залить в пустую БД тестовый сет данных, покверить, проверить результат. На более менее сложной БД это порочный подход. Какой набор тестов делать? Как бог на душу положит? Тогда как вносить изменения в проект? Кто даст какие-либо гарантии, что не сломается то, что уже работало? Допустим даже уже известен сценарий, кто-то нам явно указал ошибку. Надо написать тест на это. Часто оказывается, чтобы довести пустую базу до того состояния, когда этот сценарий проявляется надо написать кучу кода или переиспользовать куски существующих тестов. Если каждый раз писать заполнение базы с нуля, то просто утонешь в таком коде. Если переиспользовать, то запутанность и сложность тестов становится близкой к коду самой системы. Поэтому нужна некая абстрактная теория, которая скажет какие тесты писать, а какие не писать. Пока я вижу единственный кандидат на место такой теории — это проверить валидность запросов по схеме БД и выдать типизированный result set. Как только начинаешь задумываться над чем-то большем, над каким-либо более глубоким "осмысленным" тестированием, универсальность теории пропадает, получается какие-то частные случаи, которые после завтрашних изменений устаревают и начинают причинять боль. Короче, Дейкстра всё выразил в одном предложении: "Тестирование программы может весьма эффективно продемонстрировать наличие ошибок, но безнадежно неадекватно для демонстрации их отсутствия". В проект надо внести изменения. Мы можем вносить изменения, основываясь на тестах? Нет, конечно, поскольку тесты безнадежно неадекватны для демонстрации того, что мы не поломали того, что уже работало.
Если переиспользовать, то запутанность и сложность тестов становится близкой к коду самой системы.А с какого размера базами данных ты работаешь? Интересует количество таблиц и общий размер базы.
Вообще, как я себе представляю, в энтерпрайзе сделать интеграционное тестирование методом заполнения базы тестовыми данными невозможно. Думаю, обычно даже невозможно развернуть среду разработки целиком локально на машине разработчика, тем более сделать это запуском одного скрипта. Ты сам как-то писал про некий проект с ораклом, где даже невозможно было задеплоить код в базу данных. Еще все усугубляется тем, что в энтерпрайзе, в отличие от веба, средой разработки является Windows, в котором нельзя написать скрипт, запаковывающий zip архив, или запустить программу на удаленной машине.
Плюс, еще обычно сложно запускать код, потому что все тормозит (какие-нибудь энтерпрайзные веб-фреймворки, например).
Вот это реальные проблемы, а не отсутствие абстрактной теории написания тестов. Из-за этих проблем и возникают все эти идеи, типа как бы написать код, ни разу не запуская его, или запуская в ограниченном окружении выдуманных тестов.
В вебе, например, этих проблем нет. Все работает быстро (так как иначе веб-проектом никто не будет пользоваться и среда разработки простая (какой-нибудь PHP, mysql, nginx). Поэтому веб-разработчики тебя не поймут, у них таких проблем нет.
средой разработки является Windows, в котором нельзя написать скрипт, запаковывающий zip архивWTF?
Поэтому нужна некая абстрактная теория, которая скажет какие тесты писать, а какие не писать. Пока я вижу единственный кандидат на место такой теории — это проверить валидность запросов по схеме БД и выдать типизированный result set.Херовая "теория", что тут говорить.
А с какого размера базами данных ты работаешь? Интересует количество таблиц и общий размер базы.Несколько сотен таблиц, около тысячи. Сотни гигабайт.
Херовая "теория", что тут говорить.Херовая, если есть лучшая. Еще вариант, ты не знаешь как этой теорией пользоваться, поэтому для тебя она херовая.
Эта теория позволяет сделать program slicing, когда точкой интереса является поле таблицы или таблица. Большинство изменений, которые требуются заказчику, именно и крутятся вокруг небольшой горстки полей.
Все описанные проблемы у нас решены, кроме заполнения базы тестовыми данными для интеграционного тестирования. Это не сделано, потому что нет стратегии/теории как это делать. При некотором количестве связанных таблиц код для такого заполнения превращается в ненужный запутанный хлам, как по ссылке выше. И дело не в том, что мы не умеем писать код. Код мы писать умеем. Дело в том, что нет требований на основе, которых надо писать этот код. Требования стихийные, кому как бог на душу положит. А по пришествию времени, результат хорошо виден в коде. Получается тестовая база живет своей жизнью отдельно от прода. Только за жизнь базы на проде нам платят, а за жизнь тестовой базы нет.
Поэтому веб-разработчики тебя не поймут, у них таких проблем нет.Скажи, я ошибаюсь, если думаю, что у веб-разработчиков проблемы с деньгами? Если набрать на hh.ru js или php, то предлагают мало денег.
Херовая, если есть лучшая.Ложный вывод. Может быть просто полезной теории построить нельзя.
Вот есть теория как надо писать тесты. Нужно "всего лишь" написать тест на каждый путь выполнения в программе.
Это правда приводит к комбинаторному взрыву, но что уж тут поделать.
Еще вариант, ты не знаешь как этой теорией пользоваться, поэтому для тебя она херовая.Ололо, ты правда думаешь, что о каком-то rocket science говоришь?
Твоя блестящая "теория" спасет в лучшем случае от ран-тайм эксепшенов, которые будет тривиально и быстро пофиксить, и не спасет от, например, off-by-one error в параметре запроса к базе, который будет молча приводить к неверным выборкам и может жить очень долго.
Из-за этих проблем и возникают все эти идеи, типа как бы написать код, ни разу не запуская его, или запуская в ограниченном окружении выдуманных тестов.Требуется внести изменение в проект. Что ты будешь запускать? Функционал всего проекта? Нет. Тогда какое подмножество функционала ты будешь запускать? Откуда ты возьмешь эту информацию? Где информация о проекте хранится? В головах старожилов, в
Ложный вывод.Докажи, что вывод ложный. Какая у тебя мера херовости?
Ололо, ты правда думаешь, что о каком-то rocket science говоришь?
Твоя блестящая "теория" спасет в лучшем случае от ран-тайм эксепшенов, которые будет тривиально и быстро пофиксить, и не спасет от, например, off-by-one error в параметре запроса к базе, который будет молча приводить к неверным выборкам и может жить очень долго.
Rocket не rocket, но пока видно, что ты не видишь как использовать эту "теорию". Главная цель "теории" не в отлове ошибок, а в том, что на ее основе можно вносить изменения, не внося ошибок в то, что уже работает. Информация, которую получаем из этой "теории", позволяет доказывать, что изменение не вносит ошибок в то, что уже работает. Какой "теорией" пользуешься ты, когда вносишь изменения в проект?
Главная цель "теории" не в отлове ошибок, а в том, что на ее основе можно вносить изменения, не внося ошибок в то, что уже работает.Если "теория" не позволяет отлавливать ошибки, она же не позволит отлавливать регрессии, так что нет, нельзя.
Информация, которую получаем из этой "теории", позволяет доказывать, что изменение не вносит ошибок в то, что уже работает.Ой не надо только про "доказывать", как ты "доказываешь" корректность кода после изменений я помню по эпическому треду про "рефакторинг маленькими шажками".
Какой "теорией" пользуешься ты, когда вносишь изменения в проект?Никакой. Ее необходимость постулировал ты, я с этим не согласен.
Также я не пользуюь никакой абстрактной теорией о том, как надо программировать, surprise, surprise.
Вообще, как я себе представляю, в энтерпрайзе сделать интеграционное тестирование методом заполнения базы тестовыми данными невозможноЭнтерпрайз - это не библиотеки Java. Почитай определение, посмотри на примеры. Я не представляю, как нужно написать проект, чтобы в нём было невозможно заполнить базу данными для тестирования. Если всё так плохо, то и код этого проекта будет таким, что о каких-то тестах в нём можно будет только мечтать.
Думаю, обычно даже невозможно развернуть среду разработки целиком локально на машине разработчика, тем более сделать это запуском одного скрипта.Ты хотел сказать, что нельзя развернуть полностью рабочий проект? Facebook - не энтерпрайз, и я гарантрирую, что его нельзя развернуть на машине разработчика запуском одного скрипта.
Из-за этих проблем и возникают все эти идеи, типа как бы написать код, ни разу не запуская его, или запуская в ограниченном окружении выдуманных тестов. В вебе, например, этих проблем нет. Все работает быстро (так как иначе веб-проектом никто не будет пользоваться и среда разработки простая (какой-нибудь PHP, mysql, nginx).
В Facebook используются все эти технологии, и мне почему-то кажется, что код тестируется в "ограниченном окружении". Хотя глупые теории и правда не нужны, тут я с тобой полностью согласен.
Если ты не приверженец TDD, то для написания хорошего набора тестов тебе нужно представлять, как работает конкретный участок кода. Поэтому хуже всего с тестами обстоит в проектах, где даже автор не может разобраться в коде, который он только что написал. Во write only коде даже 100% покрытие тестами не даёт уверенности, что всё идёт правильно, ведь ты спокойно можешь допустить логическую ошибку и в коде, и в тесте для этого кода. Некоторые виды ошибок намного чаще устраняются во время хорошо поставленного процесса code review и/или внешнего тестирования, а не во время разработки тестов.
Никакой. Ее необходимость постулировал ты, я с этим не согласен.Ок, спрошу по другому. Как вы работаете в команде? Должны быть какие-то абстрактные принципы, которые должны быть известны всем членам команды. Как вы передаете эти принципы вновь прибывшим? Вы их рассказываете?
Так же я не пользуюь никакой абстрактной теорией о том, как надо программировать, surprise, surprise.
Все описанные проблемы у нас решены, кроме заполнения базы тестовыми данными для интеграционного тестирования.У вас каждый разработчик использует отдельный инстанс базы, или все используют один общий?
У вас каждый разработчик использует отдельный инстанс базы, или все используют один общий?Один общий.
На прошлом месте работы были многократные попытки делать свои базы у каждого разработчика. Так и не прижилось.
Тесты мне напоминают такие деревянные наскоро, сбитые хрени (леса? на которые рабочие встают, когда потолок ремонтируют. Побелили в одной комнате переходят в другую, леса убирают, а программисты хотят оставаться со вспомогательными средствами всю жизнь, и жить со строительными лесами в квартире. Вдруг потребуется потолок замазать, и лампочку удобно вкручивать, если потолки высокие.
ведь ты спокойно можешь допустить логическую ошибку и в коде, и в тесте для этого кода. Некоторые виды ошибок намного чаще устраняются во время хорошо поставленного процесса code reviewХорошо поставленное code review я описал в соседнем треде. Логические ошибки хорошо ловятся даже, если смотреть на код другого проекта.
Собственно, это отличный пример, что под code review обычно впаривают одно, а на практике это выглядит совсем иначе. Чтобы провести хорошее code review с выявлением логических ошибок надо вникать в детали задачи и фактически решать ее, а потом сравнивать свое решение с кодом. Если хотите делить деньги на двоих, вперед.
На практике, как мы видим в этом разделе, code review сводится к тому как делать indent в многострочных литералах, вопли по поводу длинных методов и т.д. Т.е. вопли, не вникая в суть задачи. Как таким ревью можно найти логические ошибки? Столько раз в этом разделе было code review моего кода, хотя бы одну логическую ошибку кто-нибудь упомянул?
Энтерпрайз - это не библиотеки Java. Почитай определение, посмотри на примеры. Я не представляю, как нужно написать проект, чтобы в нём было невозможно заполнить базу данными для тестирования.Возникла путаница в терминах. Под "энтерпрайзом" в данном треде я имею в виду софт, который имеет смысл только в рамках какой-то конкретной компании, и сильно завязан на ее бизнес-процессы и IT-инфраструктуру.
В таких условиях зачастую требуется интегрироваться с проектами, созданными другими командами разработчиков, возможно, из другой компании. Тестовый инстанс базы данных может существовать в единственном экземпляре, и его невозможно заполнить тестовыми данными для прогона тестов (Шурик подтверждает).
Кроме того, у инженеров нет политической силы, и иногда просто нет возможности принимать какие-то технологические решения. Например, надо интегрироваться с каким-то говном, хотя переписать это говно с нуля было бы легче, но переписать нельзя, потому что это под контролем другого департамента.
На прошлом месте работы были многократные попытки делать свои базы у каждого разработчика. Так и не прижилось.Чего хотели добиться, когда пытались, и почему не прижилось?
Скажи, я ошибаюсь, если думаю, что у веб-разработчиков проблемы с деньгами? Если набрать на hh.ru js или php, то предлагают мало денег.Как ты оцениваешь среднюю з/п разработчика на C#, на php и на js? Вместо средней можешь оценить выбранную тобой процентиль, нпаример.
Как ты оцениваешь среднюю з/п разработчика на C#, на php и на js? Вместо средней можешь оценить выбранную тобой процентиль, нпаример.Сейчас снова зашел на hh, похоже я ошибался.
Должны быть какие-то абстрактные принципы, которые должны быть известны всем членам команды. Как вы передаете эти принципы вновь прибывшим? Вы их рассказываете?Прямо абстрактные принципы должны быть по большей части и так известны вновь прибывшим, или как их на работу взяли? Ошибки - да, рассказываем на code-review.
На практике, как мы видим в этом разделе, code review сводится к тому как делать indent в многострочных литералах, вопли по поводу длинных методов и т.д. Т.е. вопли, не вникая в суть задачи. Как таким ревью можно найти логические ошибки? Столько раз в этом разделе было code review моего кода, хотя бы одну логическую ошибку кто-нибудь упомянул?Видишь ли, это как пирамида Маслоу. Чтобы искать логические ошибки, надо сначала чтобы код было легко читать. А если там месиво, от которого глаза разбегаются, то извини. Тем более когда за это не платят деньги и проект никому не интересен.
Отправить на работе код на доработку со словами "тут ничего не понятно, перепиши проще" - абсолютно нормально имо.
Ошибки - да, рассказываем на code-review.Мне в этом разделе никто никогда не привел код проще для решения той же задачи. Более того, подозреваю, что в задачу никто не вникал.
Тестовый инстанс базы данных может существовать в единственном экземпляре, и его невозможно заполнить тестовыми данными для прогона тестовА поднять рядом свой инстанс религия не позволит?
Кроме того, у инженеров нет политической силы, и иногда просто нет возможности принимать какие-то технологические решения. Например, надо интегрироваться с каким-то говном, хотя переписать это говно с нуля было бы легче, но переписать нельзя, потому что это под контролем другого департамента.Это не мешает написать тесты. Если код хорошо спроектирован, то с ним можно делать почти всё, что пожелаешь. Например, натаскать запросов и ответов из самого зачуханного SOAP сервиса и написать тесты, которые проверяют интеграционную часть без обращения на единственный боевой экземпляр этого сервиса. На слух это кажется сложным, но у страха глаза велики. На самом деле всё решает лень.
Оставить комментарий
kokoc88
Привожу пример использования моего подхода к разработке кода для доступа к базе данных. Пример сделан на основе модельной задачки, с которой не справился его святейшество патриарх статической типизации.Моё решение для обычных смертных. Простые вещи стараюсь делать просто. Не использую навороты, которые как бы призваны сделать разработку более безопасной, но в итоге занимают объём в два раза больше, чем прямолинейное решение вместе с несколькими юнит тестами. Задачку решил на небольшом фреймворке из семейства Dapper и ORM Lite собственного производства.
Теперь модельная (в смысле предназначенная для демонстрации кода) задачка.
1. Требуется выгрузить котэ из таблицы Cat (Id PK, Alias, Age, Ver) и истории про котэ из табицы Story (Alias PK, Story, Age) в виде фиксированных типов данных. Названия полей в типах данных могут не совпадать с названиями столбцов.
2. Отфильтровать истории при условии, что фильтр нельзя выполнить в базе данных.
3. Объединить истории и котэ по Alias при условии, что это не выгодно делать на стороне базы данных.
4. Сделать список для записи результатов объединения в таблицу CatStory(Id PK, Preview, Age где Id считывается из котэ, Preview получается с помощью применения некоторой сложной операции к паре котэ - история, а Age является суммой Age в этой же паре.
5. Выполнить MERGE USING VALUES в таблицу CatStory по Id, по результатам либо вставить новую строку, либо обновить поля Preview и Age.
6. Обновить таблицу всех котэ, устанавливая Ver = Ver + 1
7. Запросы 5 и 6 выполнить в одной транзакции с уровнем изоляции repeatable read батчами большого размера.
8. Забить на неточности, потому что задачка модельная. Но если что-то совсем не сходится - пишите, исправлю.
А теперь cенсация! Пост в Development с примером кода!