Как стать автором
Обновить

Комментарии 531

То есть ты взял и превратил монгу в тупой key/value store. Если бы в примере была реляционная БД, выглядело бы еще нелепее.
Ну, почему же. Если что-то еще нужно от базы — нужно реализовать аналогично. А если надо перейти на реляционные — то это должно быть прозрачно для пользователя, и выглядело бы точно также.
Подобная абстракция приводит все базы данных к общему знаменателю. И либо ты получаешь оракл, работающий как k/v store, либо переход не будет «прозрачным для пользователя».

Диагностирую здесь синдром архитектора :)
Ну давайте подумаем. Что с точки зрения ООП нужно от БД? Правильно только сохранение и восстановление объектов. Это тут и показано. Если что-то нужно еще — то нужна уже реляционная база, и там нужно добавить вызов хранимых-процедур. И все.
Хранимые процедуры есть далеко не везде. Там где есть — сильно различаются по возможностям.

А что такое «точка зрения ООП» — мне непонятно. Есть точка зрения приложения. Приложению нужно получать данные. Мне сложно представить себе такое хоть сколько-нибудь полезное приложение, которому будет достаточно такого вот урезанного доступа к данным.

Именно имея такое приложение, я и решил попробовать MongoDB. Ну, и потом, все что нужно дополнительно можно легко дореализовать. Но речь идет не о том, сколько всего нужно, а о том, как к этому всему легко и естественно обращаться.
Реляционная база данных — это не хранимые процедуры, о чем вы???? MуSQL долгое время жил без таковых, но при этом был реляционной.
Реляционная база данных — это связи между обеъктами и выполенение четырех принципов ACID.

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

Как только мне понадобится делать сложные выборки — я перейду на реляционную базу данных и напишу select в хранимой процедуре. И не буду никогда заниматься глупостями в виде LINQ.

Транзакции на уровне приложения — тоже бред, им место в хранимых процедурах.

Но и потом, я же не говорю, что то что я тут реализовал достаточно на все случаи жизни. Но все это легко дореализуется в подобном стиле.
«Что с точки зрения ООП нужно от БД? Правильно только сохранение и восстановление объектов. „
То есть поиск не нужен? Типовая операция “найди все контракты за этот год» в ООП никак не отражается?
Нет, это проблематика баз данных.
С какой это радости? Это бизнес-операция, выросшая из бизнес-потребности, почему она не отражается в коде приложения?

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

Ну и да, вы проигнорировали первый вопрос. На всякий случай поясню: вот у меня есть бизнес-приложение, в виде, скажем, веб-сайта. Оно, натуральным образом, состоит из слоев (layer, не tier): представление, бизнес-логика, хранилище. Соответственно, представление и бизнес-логика реализованы в рамках ООП (это условия данной задачи, а не всемирное правило).

Теперь, внимание, вопрос: как так выходит, что решаемая пользователем бизнес-задача («найти все контракты за год») есть в представлении (иначе пользователь ее не решит), есть в бизнес-логике (представление контактирует только с бизнес-логикой), но не имеет выражения в ООП? У нас в приложении внезапно есть не-ООП часть?
Давайте договоримся на берегу, что БД — у нас реляционная. Так?

Тогда я вас не понимаю, вы никогда не догадывались, что работа реляционной базы является не-ООП частью?
Или вы не знаете, что обращение к базам по определению не является объектным (а всего лишь где-то лучше/где-то хуже эммулирует объектный стиль)?
И кстати, именно поэтому я предлагаю использовать хранимые процедуры, т.к. их вызов наименее разрушает ООП-стиль
Вы просто никогда не админили сколько-нибудь сложную базу с хранимыми процедурами.
Никакой ООП-пуризм не стоит мучений от внезапной необходимости обновить и подлатать хранимые процедуры на полудюжине боевых узлов БД работающего проекта.
Дорогой мой, я конечно не админ, но наш продукт стоит на MS SQL Server, и никто пока из админов не жаловался на такие высосанные из пальца проблемы. А обновления мы шлем достаточно часто.
Одно-единственное развёртывание продукта, никаких «маленьких патчей для больших клиентов», никакого шардинга (судя по всему) — да просто ваши админы в раю живут, можно сказать:).
Ну не знаю, если кому-то сильно приперло — то есть понятие транзакции :)
Вот-вот.
И на одном шарде из20, скажем, транзакция подвисла и отвалилась из-за непредвиденного чего-то.
Ура! Теперь у вас есть 19 серверов с одной версией хранимой процедуры и один — с другой. Осталось найти, какая где.

Плюс очевидный момент — изменение архитектуры таблиц это долгий и дорогой процесс. Не надо этого делать практически никогда. Собственно, из этих посылок и появилась профессия DBA (который Architect).
Обновление версии на сервере приложений — гораздо более дешёвый манёвр, и гораздо лучше отработанный. Имеет смысл ограничиваться им по возможности.

Ну и да, я вот толком не понимаю как хранимые процедуры тестировать. То есть с CIв таком подходе тоже сложности получаются.
> Обновление версии на 20 серверах приложений

Спорно, но тут моя компетенция кончается…
это фраза должна была появиться в голове еще до написания статьи
А вам бы вообще молчать, так как не понимаете о чем пишите
«Тогда я вас не понимаю, вы никогда не догадывались, что работа реляционной базы является не-ООП частью?»
Ненене, вы не торопитесь, вам до БД еще надо два слоя пройти.

«Или вы не знаете, что обращение к базам по определению не является объектным (а всего лишь где-то лучше/где-то хуже эммулирует объектный стиль)? „
А то, что вы пишете выше (в посте) и ниже (в комментариях, “мыслите объектно») — это что тогда?

Если же мы говорим о БД-объектной, такой о какой идет речь здесь MongoDB.

То я как раз показал паттерн, где обращение к ней будет объектным.

Тут же предлагаю использовать LINQ — которое совершенно противоречит объектности. И под каким бы соусом это не подавалось бы — оно объектным не станет.

Производители MongoDB не предоставляют мне истинно объектного способа использовать их объектную базу. Поэтому я и пишу свои адаптеры. Можно написать адаптер и к вашей задачи «найти все контракты за год». Нужно лишь в объекте Контракты ввести понятие полей-ограничений, и подгружать только те объекты которые соответствуют эти условиям. Вот тогда то и будет объектная логика загрузки. Сейчас же производители объектных баз по привычке размышляют в терминах select запроса, отсюда и корявость.
С точки зрения ООП. Такая операция реализуется так: получаем идентификацию всех контрактов (куда входят даты) — и потом уже получив пробегаем по ним отбирая нужные. Но так как на стороне клиента — это делается медленно, то такие запросы выполняют базы данных на стороне сервера.
То есть вы предлагаете отказаться от столь любимого вами ООП ради быстродействия?

Впрочем, мне интересно: а вы в курсе, что в том же .net уже давно есть технологии, позволяющие решать подобные задачи не отклоняясь от ООП, но при этом с быстродействием операции, выполняемой на стороне СУБД?
> не отклоняясь от ООП

О чем речь, думаю у нас разные оценки этих «технологий»
Вот об этом, конечно же:
[...]
return AllContracts.Having(contract => contract.Date.Year == 2005)
[...]
Снова LINQ мне хотите подсунуть? Или вы действительно думаете, что этот код соответствует ООП?
Ваш код соответствует ООП в гораздо меньшей степени. Оттого, что в коде есть слово class, код не станет ООП. Как и использование рефлексии его таковым не сделает.
Что в моем коде не соответствует ООП, а то ля-ля все горазды
Всё. Начиная с копипасты и заканчивая рефлексией
Видите ли, рефлексия используется в одном конкретном месте. Вы же предлагаете что? Загадить весь проект обращением к базе вместо этого так?
Я предлагаю почитать фаулера в частности раздел с паттерном Repository. А потом домашнее задание — описать репозиторий так, чтобы он не зависел от типа хранилища. И не будет обращений к базе данных, и будет ООП во все поля и мир во всем мире.
> описать репозиторий так, чтобы он не зависел от типа хранилища

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

Так паттерн Репозиторий тут не лучший вариант, в то время как мне легко поменять объект Database, на DatabaseNew и все.
Да, чтобы взять и поменять монго на другое хранилище.

Паттерн Репозиторий никак вам не помешает перейти на другое хранилище. Подсказка. Репозиторий не синглтон.

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

Задача с двумя звездочками. Реализуйте сохранение разных частей одного объекта в разные хранилища.
Да легко же. Я же вам и говорю, делаю наследников от Database — и все. А TaskManager решает когда подменить ссылку на текущую базу. Что может быть проще?

Ваш же паттерн Репозиторий предлагает стругать интерфейсы — нафиг не кому не нужны, да к тому, же тогда нет общего класса типа Database, где находится общая логика всех хранилищ. Я уже это не раз повторял тут и в прошлой статье.

P.S. И не надо считать себя шибко умным и говорить в превосходной форме — не солидно. Я уж и поболее знаю, но ведь не выпендриваюсь, а?
> Я уж и поболее знаю, но ведь не выпендриваюсь, а?
Взаимоисключающие параграфы. You've made my day ©.

Если вы не понимаете, для чего нужны интерфейсы, то это только ваши проблемы.

Еще раз, как сохранить разные объекты в разные хранилища (одновременно используется 2 хранилища в приложении, да-да, бывает и такое). Я так понимаю, что наследоваться от разных Database? А что тогда делать с таск-менеджером?
А как сохранить разные части одного объекта в разные хранилища. Ответа я не получил.
«Ваш же паттерн Репозиторий предлагает стругать интерфейсы — нафиг не кому не нужны, да к тому, же тогда нет общего класса типа Database, где находится общая логика всех хранилищ»
Простите, а на основании какого определения паттерна Repository вы делаете такие выводы? Есть прекрасные обобщенные решения Repository{of T}. А интерфейсы вообще в паттерне никак не упоминаются, они берутся в конкретных реализациях, и обычно только из желания сразу сделать удобную абстракцию для юнит-тестирования.
Причем рефлексия — вполне объектна
Хотя согласен, что нужна только тогда, без нее не обойтись. Тут не обойтись. Хотя есть близкое решение на обобщенных классах, но не то см. ниже в комментариях вариант от lair
А почему нет? AllContracts — объект класса «коллекция контрактов», имеющий метод «все, удовлетворяющие условию», условие передается специлизированным классом (паттерн QueryObject).

Возражения?

(Заметим, LINQ тут не при чем)
Вот, если бы вы это так написали, другое дело, но у вас оператор лямда выражения =>… перепишите без него, тогда посмотрим.
Гм. А как по-вашему, во что разворачивается «оператор лямбда-выражения»?
Во уж не знаю… но его не разворачивать нужно, а убрать эту глупую нотацию
То есть, вы не разобравшись, что это такое, считаете, что имеете право судить о том нужно оно или нет?
А на хрена мне знать как работает хрень?
Вероятно, чтобы не изобретать хрень самому?
Это типа посмотреть как хрено предлагают сделать, чтобы так не делать. Не, уже насмотрелся на разные хрени, экономлю время теперь даже на просмотр, задницей уже чую где дрянь.
Судя по вашему коду, вы много чего задницей делаете ;)
А судя по вашей болтовне, вы много только языком…
Ну да, девушкам нравится; р
Я этого хабраюзера лично знаю и да, он на многое способен, в том числе и языком. В смысле объяснить. А вам рекомендую долбить матчасть — полезная вещь.
Я так и думал, что не знаете. Почитайте Джона Скита, C# in Depth, раздел 9.3, Expression Trees. После этого вы узнаете, что лямбда-выражение может разворачиваться в AST, то есть — аналог Query Object.

Я, очевидно, могу переписать этот код на чистые Expressions, но это будет громоздко. Результат все равно будет ровно тем же самым. Эта, как вы выражаетесь, «глупая нотация» сэкономила мне шесть-восемь строчек кода.

Вы же foreach используете? Или тоже вместо него явно обращаетесь к методам IEnumerator?
О! В этом и дело — «громоздко». А у меня в реализации нет!
У вас в реализации и поиска нет. Так что сравнивать бесполезно.
Я вам показал как это реализовывать, поэтому сравнивать легко.
Поиск? С произвольными критериями? У вас этого нет в коде нигде.
А с произвольными критериями — никому на практике не нужно! Запомните это. Поиск идет по идентификации, или по ряду типовых ограничений в виде периода по дате.

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

Читайте самое начало, предложение было сделано для задач с небольшим объемом. На большие объектные базы не годятся. И нечего тут насиловать продукты, которые не для этого.
tac, вы так любите хранимки… А расскажите как вы версии хранимок контролируете и на продакш их выкладываете. Правда, интересно ;)
Вот были бы вы не троль, я бы вам рассказал… а так зачем?

Вы как детё, честное слово, хранимки — это текст в .sql файле. Что такое SVN слышали? Для файлов годится? А зачем спрашивать?

Или не умеете запускать .sql скрипты на базу?
Ага. А как вы, бишь, гарантируете порядок накатки и целостность базы?
Снова не существующую проблему придумали :) запускается же не вовремя работы пользователей.
скажите это проектам с аптаймом в 99.9 процентов.
Еще раз целостность гарантирует транзакция, но выполнять горячие копирование — это вообще-то бред. Нет таких «больших клиентов», которые не могут подождать до утра.
поверьте, есть такие клиенты. мир не ограничивается миром Ынтырпрайз ПО. Попробуйте например систему управления работой скорой помощи попросить подождать до утра пока ваша база «безопасно обновляется»
Вы о транзакциях слышали? Так в чем проблема?
вам уже выше описали. например, случай с шардингом. кроме того обновление в транзакции вполне может вызвать (и скорее всего вызовет) блокировку и задержку работы. хотя о чем я говорю. ведь ваша библиотека не для таких случаев, она же для одного пользователя максимум.
«Еще раз целостность гарантирует транзакция,»
Не гарантирует. Когда вы накатываете обновление, состоящее из полусотни скриптов на нескольких связанных SQL-серверах, никакие транзакции не помогут.

«Нет таких «больших клиентов», которые не могут подождать до утра. „
Это у вас их нет.
А какая разница, когда запускается? Порядок-то все равно должен быть фиксированный, и целостность базы все равно надо гарантировать.
Порядок чего? Сколько я помню нет таких хранимых процедур, которые закидывать надо в нужном порядке. Но если сильно нужно, ну выполните это в транзакции, только по мне это всегда практически лишние.
«Сколько я помню нет таких хранимых процедур, которые закидывать надо в нужном порядке.»
Ну, хранимые процедуры, прямо скажем, вообще весьма к этому пофигистичны. Но надо же быть честным и понимать, что модификации БД хранимыми процедурами не ограничиваются.
Ну, блин, вы уж вначале определитесь или мы за хранимые процедуры говорим, или за все администрирование БД
А это сильно взаимосвязанные вещи. Прямо скажем, одно — часть другого.
И как, всегда все легко накатывается/откатывается?
«А с произвольными критериями — никому на практике не нужно!»
640k достаточно всем, да?

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

«Как только нужен будет поиск более серьезный — нужно взять и перейти на SQL, написав хранимую процедуру. „
Вот ведь уперлись вы в хранимые процедуры… зачем писать хранимую процедуру, когда нужно простое условие отбора по двум-трем полям?

“Читайте самое начало, предложение было сделано для задач с небольшим объемом.»
А при чем тут объемы вообще? я о задачах говорю.

Ну и да, ваше решение — оно, типа, универсальное, статья ведь называется «Отделение логики базы данных», а не «работа с объектными БД».
А сейчас вы говорите — совсем о других задачах. Я такие не решаю, просто за то, что заказчику «надо было искать по дате, а сегодня — по категории» — я беру дополнительные деньги, и изменяю select и GUI. Если же вы говорите о динамической подмене без вашего присутствия — это другого рода задачи. Я о них не говорил.
«Я такие не решаю, просто за то, что заказчику «надо было искать по дате, а сегодня — по категории» — я беру дополнительные деньги, и изменяю select и GUI.»
Угу. Вот только заказчик, когда стоит выбор между изменением с ценой около рабочего дня и изменением с ценой около недели, выбирает почему-то первых исполнителей.
А так все таки у вас тоже не полностью автоматизировано. Тогда лирика.

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

«А вот сделать доработку в предлагаемой мной идеологии будет как раз быстрее и естественнее. „
Естественнее лично вам? Может быть, с этим спорить сложно. Естественнее c#-разработчику, не имеющему SQL-опыта? Вряд ли.

Ну а что касается “быстрее» — тут вы заведомо неправы, потому что в вашем случае надо менять (и развертывать) два фрагмента системы вместо одного.
Да, конечно, я всегда готов отказаться от ООП ради быстродействия.
Правда только тогда, когда быстродействие реально нужно, а не впрок
Еще: может я, конечно, что-то и забыл, но этот обильный рефлекшон не пойдет на пользу производительности :)
И это заметьте в Java, а C# вообще-то должно быть еще лучше… но надо проверять (еще не добрался)
Что-то я не пойму… автор пытается, типа, переизобрести/имплементировать ActiveRecords?
В комментариях к своей первой статье автор отказался читать про паттерны типа Repository или Table и уперся в изобретение свеогое квадрато-колесого велосипеда и данная статья призвана была пояснить его мысль.
Дорогой мой, не стоит выдавать свои соображения за мои. Я в курсе этих паттернов — но не считаю их более удобным, чем то, что я здесь предложил.
Почему же тогда ваши топики одни за другим набирают минусы и комментарии непонимния? /Если что, это риторический вопрос, не требующий ответа.../
Я тоже в недоумении, почему многие люди не понимают элементарных вещей и минусуют, без объяснения претензий ?!
Вам все в комментариях тут и там все доходно объясняют, только хотите ли вы это слушать? может это вы недопонимаете элементарных вещей?
Где?
Ну, и какой по вашему элементарной вещи я не понимаю?
Можете ли Вы предложить пример как надо сохранить объект в MongoDB, покажите, тогда поговорим чем от лучше/хуже. А так вы пока воздух сотрясаете, и не более того
Правильнее сказать применить ActiveRecords с суровой действительности. Впрочем думаю отличия есть.
А можно минусующих попросить представить хоть маломальские аргументы?
Хабр уже не торт, школота набежала и всё такое. Забей. Пойти и децл покодить будет гораздо более полезным времяпрепровождением. :)

Я вот не вижу смысла сюда писать статьи.
Ок, школота — так школота. Думал тут умеют серьезно обсуждать
Читать Фаулера. До полного просветления. И не изобретать очередной велосипед.
А Вы хоть попробуйте рассказать, что у него может быть лучше?
tac, я вас уже знаю, поэтому спорить не буду ни о чем ;)))
Просто оставлю вам ссылку:

martinfowler.com/eaaCatalog/repository.html
Ок, но паттерн Репозиторий — на порядок хуже.
Вот смотрим хотя бы на эту реализацию паттерна Репозиторий

И вы хотите меня убедить что обращение

using (var dataContext = new HotelsDataContext())
{
var hotelRepository = new Repository(dataContext);
var cityRepository = new Repository(dataContext);

City city = cityRepository
.SearchFor(c => c.Name.StartsWith("Ams"))
.Single();


чем то лучше

City city = new City("Ams");
city.Load();

По коду выше я понимаю, что именно этот код делает.
По нижнему — нет.

Поэтому да, код выше — лучше.
Ваши привычки тут не имеют ни какой роли. А привязывать и раскидывать типо-зависимый код по проекту — это просто ужас, а не код.
Жжете ;)
Да уж, с несерьезными людьми — говорить серьезно не получается.
Вот тут я с вами внезапно полностью соглашусь ;)
Пишите бредокод дальше, не буду вам мешать.
Спасибо, вы мне как-то и раньше не мешали ;)))
Если точнее

City city = new City( 0, «Ams_»);
city = city.Load();
Это как же надо привыкнуть писать не естественные обращения к объектам, чтобы элементарное сохранение объекта, осуществлять путем написания кучи странных строк кода, вместо того чтобы сказать

city.Save() и забыть. И эти люди меня еще чему то учат ;)

Вот оно. Казалось бы, объектные базы такие как MongoDB должны лишь способствовать усвоению теории ООП. Но по факту оказывается, что люди как не знали, так и не знают. Так зачем же вам MongoDB, если оно вам не способствует приведению кода к ООП стилю?
Я конечно не такой талантище в области ООП как вы, о хитроумнейший tac, но будет ли мне позволено заметить, что ваш код мало того что жестоко насилует принципы SOLID (что еще простительно), но и с громким грохотом упадет при первом же race condition, разметав по километровой окрестности мозги того бедолаги, которому придется поддерживать этот код?
Да, глупости вы говорите, что он нарушает? Какие именно проблемы вы видите? Ну, и не надо предъявлять претензии к побочным вещам, когда речь сейчас идет о другом.
Если переписать ваш код так, чтобы он был понятен и привычен нам, убогим быдлокодерам, то внезапно получится тот «репозитарий» про который вам регулярно напоминают.
Ну, перепишите — посмотрим, что получится :)
За меня это уже сделал майкрософт. Entity Framework, называется.
Репозиторий, пожалуй, был в прошлой статье. В этой уже активрекорд.
Имхо, до активрекорд тут далеко.
Но ближе к ней чем к чему-либо ещё.
Эти две статьи про одно и тоже :)
Скажем так, вбирает в себя лучшие качества Репозиторий + активрекорд.
Что может быть проще логики, создать объект в котором поместить условия поиска и выполнить загрузку объекта, которому соответствуют искомые параметры.

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

У прикладного разработчика не должно быть понятия «работаю с базой». Есть лишь понятие «работаю с объектами», как они сохраняются/восстанавливаются — это не его уровень компетенции. Тот кто будет настраивать работу приложения (через класс TaskManager) — он её прозрачно настроит.
Это у вас в джаве так принято? Я то вот как-то привык что разработчики обычно компетентны. А некомпетентные не нужны :)
Причем тут джава? Код на шарпе :)

Причем тут компетентность разработчика? Речь идет о создании естественного интерфейса базовых классов. Вы похоже не догоняете, что код обросший прямыми обращениями к базе не куда не годен?
Естественный — ради бога. Вот как выглядит естесственный интерфейс

user = User.find_by_email("me@example.com");

user_orders = Order.where(user_id: user.id)


А то что у тебя — это онанизм какой-то :)
Да, на всякий случай, в коде класса User _нет_ метода find_by_email, а в коде класса Order — метода where. Это всё «из коробки».
И что тут естественного?
Всё. До последней буквы. Нет ничего лишнего. Код читается как художественный текст. А теперь давай посмотрим, как это выразить в твоей парадигме.
user = new user (0,"",«me@example.com»).Load();

user_orders = new order(user.id).Load();
Ну и че это за 0 и ""? И нахера мне у нового объекта вызывать Load. Я ж его уже создал, не?
Это параметры конструктора. Создали вы объект для поиска, и вам теперь нужно загрузить его полные данные.

Не знаю от куда нотация

user = User.find_by_email(«me@example.com»);

Но она видимо предполагает создание специального статического метода find_by_email — зачем?
Я не хочу создавать объект для поиска, это неестественно. Я хочу сразу получить объект.

Язык — или руби или питон. Специального статического метода там не нужно, курите метапрограммирование.
Ладно все ясно — ваших глупостей я наслушался.
Если что — в сишарпе, начиная с 4-го тоже так можно сделать ;)

А вообще, tac — вам пора придумывать свой язык программирования. Правильный, умный и воистину объектно-ориентированный. Куда ж до вас майкрософту ;)
Руби :)
Метод find_by_email — это сахар для

User.where(email: "me@example.com").first


Причем он нигде не определен и писать его не нужно. В похожей манере можно переписать вторую строчку

Order.find_all_by_user_id(user.id)
Дорогой, мы говорим на C#, все остальное меня не интересует.
Не знаю как ты, а я говорю про естественные интерфейсы. В общем ладно, история всех расставит по своим местам :)
Естественные интерфейсы? На каком это языке?
В четвертом сишарпе можно сделать то же самое. И даже через такой же механизм ;)))
Продемонстрируй, тогда поговорим
Ты что творишь? Представляешь, какого говнокода он теперь сможет натворить? :)

(если освоит, конечно)
Не думаю. Он сейчас скажет, что майкрософт — идиоты и придумали ненужную хрень.
А можно от туда выбрать, именно то о чем мы говорим, и написать на наших примерах.
Не могу лишить вас удовольствия хоть что-нибудь почитать.
> Дорогой

Хамите, парниша :)
По крайней мере, на «ты» я с Вами не переходил
Ути-пути, какие мы вежливые. :)

Ну и объясните мне как появляется метод find_by_email — если его не надо определять и писать?
method_missing и немного светлой магии. Читай ссылку от retran.
Но вы все равно пишите метод, реализующий это. Польза этого сильна сомнительна. Аналогично макросам в С++, что лучше не использовать.
Я же говорил ;)
Но она видимо предполагает создание специального статического метода find_by_email — зачем?

Чтобы найти пользователя с данным email :) При этом мы можем прозрачно использовать все нюансы системы хранения (вплоть до хранимых процедур в БД), и в то же время быстро её поменять, изменив только код инициализации или конфиг.
А в динамических языках система сама нам этот метод и «напишет».
user_id — это первичный ключ у ордера? Не было таких установок. Вот дополнение

user_orders = Order.where(user: user.id, is_archived: false)
tac, я вас поздравляю. Вы наконец-то объяснили убогим хабрахомячках зачем нужен DAL и куча паттернов его реализации.
Я вам всего лишь показал одну (лучшую реализацию) DAL
Все еще продолжаете жечь ;)
А у вас пока нет серьезных возражений, так почему же не словить лулзов… как только разговор станет серьезнее, так и я начну говорить
tac, я вам хочу сказать «спасибо». Я с ваших статей ловлю лулзов и поднимаю себе настроение больше чем от чего-либо еще на хабре ;)

А серьезных аргументов и не будет пока вы не начнете видеть менее серьезные.
«Что может быть проще логики, создать объект в котором поместить условия поиска и выполнить загрузку объекта, которому соответствуют искомые параметры. „
Угу. Вот только это должен быть другой объект, нежели тот, который вы загружаете и сохраняете. И объясняется это очень просто: бизнес-сущность ничего не знает о том, что она хранится в БД. Ей это не надо. Ее интерфейс — это те бизнес-действия, которые она совершает. Это как книга, которая ничего не знает о шкафу, в котором она стоит.

Соответственно, принцип единой ответственности говорит нам: положите бизнес-ответственности в бизнес-объект, а ответственности по хранению — в объект хранилища.

Это как раз и есть “мыслить объектно».
Нет, если бы вы читали Буча, то знали бы, что сохранность объектов — это одно из их свойств. Т.е. именно объект и должен уметь сохраняться. Как именно, так он и не знает — это как решит TaskManager
Вообще-то, Буч пишет о persistance в другом контексте немного.

И не забывайте про доменную модель, без нее никуда, а в ней нет persistance.
а это что за жесть?

m.Name == «GetCollection»
А попробуйте понять почему метод GetCollection имеет такую строку :)
Вам намекают, что искать метод перебором — говнокод, и что «магические» строки в коде — еще больший говнокод.
Серьезно? А вы вот возьмете, такой умный и найдите этот метод не перебором
Вперед, попробуй применить и получить тот же результат. Ну, что за дети, честное слово…
Там уже есть пример, учимся читать ВНИМАТЕЛЬНО.
Вначале думаем, потом пишем… Пример в студию, и проверяем как он работает… учитесь дальше
Опять жжете ;)
Это вы никак не поймете, что ваши примеры тут не работают. Ладно, слили, так слили…
«Начнем с нашего управляющего класса в нашем ПО.»
А зачем он нужен? Без него обойтись никак?

Ну и да, тестируемость этого класса _и всех его потребителей_ на высоте: «thisInstance = this; currentDatabase = new Database();». (Кстати, вас не смущает, что вы инициализируете статическое поле в конструкторе экземпляра? Типовая реализация паттерна Singleton не для вас?)

«MethodInfo[] myMethod = locType.GetMethods();
foreach (MethodInfo m in myMethod)
{
if (m.Name == „GetCollection“)»
LINQ тоже не для вас. Хотя он бы повысил читаемость вашего «поиска метода» в несколько раз.

Ну а весь ваш класс DBData — это нарушение принципа persistance ignorance, о котором в интернетах написано существенно больше десяти раз. Но ладно бы persistance ignorance, у вас еще и нарушается SRP: ваш DBData содержит методы Count, Load и LoadAllID, которые работают не с самим объектом, у которого вызываются, а с чем-то другим, а от объекта используют только тип.

«StrategiesData SD = new StrategiesData(1, „Test1“); SD.Save();»
Наглядная демонстрация всех проблем вашего TaskManager. Как вы собираетесь делать изолированный тест этого кода?

Ну и да. Во-первых, вы продолжаете изобретать паттерн Repository. А во-вторых, весь ваш DBData и Database можно было бы переписать на дженерики, и не возиться с рефлекшном (который вы даже не кэшируете к вашему стыду).
Ну давайте разбираться.

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

> LINQ тоже не для вас.
Это уж точно. Пора запомнить, что этот ненаглядный бред я не использую.

>ваш класс DBData — это нарушение принципа persistance ignorance
Ложь. Он как раз обеспечивает независимость логики хранения

> ваш DBData содержит методы Count, Load и LoadAllID, которые работают не с самим объектом

Зато вызывающий действительно прозрачно работает с базой. А эти метод классический паттерн Адаптера, в чем проблемы ни какой нет.

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

> весь ваш DBData и Database можно было бы переписать на дженерики, и не возиться с рефлекшном
Попробуйте это сделать. Не получится.
«Не обойтись. Нужен как управляющий класс, который решает какую базу использовать.»
Слова DI-container и Dependency Resolver вам ничего не говорят?

«Это уж точно. Пора запомнить, что этот ненаглядный бред я не использую.»
Вы, видимо, не умеете читать по-английски? Потому что LINQ-запрос — это просто английский текст.

«Ложь. Он как раз обеспечивает независимость логики хранения»
Вы путаете «независимость» и «незнание». Бизнес-объекты не должны знать о своей персистентности.

«А эти метод классический паттерн Адаптера, в чем проблемы ни какой нет. „
Простите, а что делает адаптер в бизнес-объекте?

“Что Вы предложите взамен? Чтобы вызывающий эти методы, заботился о получении ссылки на базу и работал с этими методами через базу? Так вот это как раз и есть плохой стиль.»
Ну, если писать в _вашем_ стиле (обратите внимание, что вы пропустили все вопросы, связанные со стилем кода), то такие вещи делаются статиками на соответствующем классе. Это как минимум. На самом же деле, они должны быть у соответствующих репозиториев (а вот получать на них ссылки, или использовать, как вы, глобальные объекты — вопрос второй).

«Попробуйте это сделать. Не получится.»
Можно было на деньги поспорить.

pastebin.com/4BjGuwGW
> Можно было на деньги поспорить.

Да, но согласитесь, что

public class Person: EntityBaseсильно не естественно в отличии от

public class Person: EntityBase
А кого это волнует? Рефлексия не более естественна, но это — работает без кучи мусорного нетипизованного кода.

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

А это волнует! Выше я уже пояснял, рефлексия только в одном конкретном месте, и она написали и забыли. А вот объекты нужно всегда создавать новые, и нужно специально научить разработчиков делать менее естественное наследование.
«По остальным пунктам лень пока.»
Новая, неожиданная версия отмазки.

«А вот объекты нужно всегда создавать новые, и нужно специально научить разработчиков делать менее естественное наследование. „
Это уже другой разговор. Изначально речь шла о том, реализуемо это, или нет. Как видите, реализуемо.

А если речь идет об удобстве разработчика, то, повторюсь, ваш подход неудобен весь целиком, и именно из-за этого и вырастают такие костыли.
«По остальным пунктам лень пока.»
Так в итоге и не ответили. Зачет.

А ведь я там перечислял конкретные ошибки в вашем коде.
съелось, речь о

public class Person : EntityBase<Person>
Попробую пояснить, почему этот код плох без отсылок к книжкам Фаулера или стадартным шаблонам типа ActiveRecord/DomainObject+DataMapper.

У него ужасное соотношение сигнал-шум.
Ну зачем, скажите мне, заниматься вот этим вот? Только чтобы использовать reflection? Показать, что вы это умеете?

MethodInfo locMethodGetCollection = GetCollection(argObject);
var locCollection = locMethodGetCollection.Invoke(db, null);
MethodInfo locMethodLoad = GetMethod(locCollection, «FindOne», 1, «Object»);
object[] locArgs = { new { ID = argObject.ID } };
return (DBData)locMethodLoad.Invoke(locCollection, locArgs);

MethodInfo locMethodGetCollection = GetCollection(argObject);
var locCollection = locMethodGetCollection.Invoke(db, null);
MethodInfo locMethodDelete = GetMethod(locCollection, «Delete», 1, «Object»);
object[] locArgs = { new { ID = argObject.ID } };
locMethodDelete.Invoke(locCollection, locArgs);

Зачем методы перебирать?

Я вначале подумал, что вы дурак, tac, но я посмотрел ваши статьи. Вы не дурак, tac, нет. У вас интересные хорошие статьи. Видимо, дело в том, что просто программирование — не для вас. В этом нет ничего страшного или плохого, все люди разные. Не нужно насиловать себя и языки программирования на которых вы пишете. Бросайте это глупое занятие, tac, подберите себе лучше занятие по душе. Может быть, через несколько лет, именно вы найдёте лекарство от рака или же возглавите колонизацию Марса.
И это Вы будите оценивать, что программирование не для меня :)

Ну давайте разберемся

> Зачем методы перебирать?

А Вы подумайте! Вы по другому это не сделаете! Надеюсь не сольете, как выше retran, а на самом деле попробуете и поймете.
Ага не сделали, вы забыли, что нужен метод не «Test», а FindOne у которого есть один параметр типа Object, а не другие его перегрузки
А теперь нужно, чтобы метод этот был generic
А он разве не дженерик? Еще раз — учитесь читать внимательно.
Стыдно должно быть. Методы FindOne оба дженерики.

public static string FindOne<T>(string arg) {}
public static string FindOne<T>(object arg) {}
typeof (GenericMethodContainer).GetMethod(«FindOne», new Type[] {typeof(Object)})
Научитесь уже читать документацию
Во. А это интересно. Попробуем.
Тьфу, нет не интересно, я то думал это вы метод generic, нашли… а это у вас он так называется GenericMethodContainer

Пробуйте дальше. Что делать когда нужен найти метод с сигнатурой

private ArrayList LoadAllID<T>(DBData argObject) where T:DBData



Во-первых, в вашем коде дженериков нет, а тут вы их требуете.
Во-вторых, что же вы тогда вообще свой парсер C# не написали. Рефлексия это слишком просто для вас.
Во-третьиъ, вспоминается история с буханкой хлеба и проволокой.

Опять же, 5 минут гугления
MethodInfo method = typeof(YourType).GetMethod(«LoadAll», new Type[] {typeof(DBData)});
MethodInfo generic = method.MakeGenericMethod(YourT);

Что не меняет того факта, что это нахрен не нужно
Вы свои обрывки хотя проверяете? Не работающий код, вырванный кусками — тут не пройдет. Нашли метод MakeGenericMethod — молодец. Теперь осталось понять что он делает. Внимательно прочли бы мою статью, нашли бы раньше, и может быть поняли бы.

Но уже на первой строчке вы получите иксепшен, т.к. могут существовать два метода LoadAll — один обычный, другой обобщенный.
Ну это уж извините, на сишарпе не пишу, и компилятора не имею. Методом простого гугления я нашел то, что несколько ваших копипаст по 15 строк легко заменяются одним методом. вы начали требовать того, чего у вас в коде нет.
Это надо было сказать в самом начале. А не учить тому, чего сами не знаете.

Ну, гуглить то вы может и научились, а программировать еще вопрос.

Я всего лишь спросил вас как вы по другому реализуете именно то, что есть в коде см. метод private MethodInfo GetCollection(object argObject). Там именно то, что нужно.

Вы думаете я не знал найденного вами способа? Нет, дорогой, я прекрасно знал. Но избрал я другой способ, т.к. он более общий, и подходит на все случаи жизни. Да, я поленился обобщать, то что не до конца понятна в какую сторону обобщать. Останавливаться на нужном уровне обобщения вам поможет практика. Но имея поиск по циклу, мне более легко поддерживать такой код, дальше его обобщать, мало ли какие методы в базе Mongo мне потребуется найти. Так я ставлю нужное дополнительное условие и получаю результат. А вы вначале все равно развертываете поиск по циклу. Поэтому принебрежение к копипасте — это вас в школе выдрисовали, но увы лишь в одну сторону и вы не знаете когда она допустипа и лучше чем «гладкий» код. И вс ледующий раз малыш думай прежде чем гнать на дядю.

facepalm.jpg.to
Вам выше уже предложили готовое решение. Но оно вас чем-то (и вы не говорите, чем) не устраивает.

Напоминаю еще раз: pastebin.com/mYwS8f1J

Есть ровно один сценарий, когда это не сработает: когда у вас дженерик-метод с параметром, тип которого задается самим дженериком (т.е. Save{of T}(T data)). Вот _это_ нельзя найти через GetMethod. Но вы этот сценарий нигде не озвучивали.
msdn.microsoft.com/ru-ru/library/6hy0h0z1.aspx

mInfo = typeof(Program).GetMethod(«MethodA»,
new Type[] { typeof(int), typeof(int) });

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

Про RC в вашем замечательном TaskManager вам уже рассказали. Есть типовая реализация синглтона (если уж возникла жуткая необходимость его использовать) защищенная от этих ошибок. Но это видимо не для вас.

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

Про нарушение принципов SOLID вам тоже уже видимо сказали. Но добавлю еще от себя. С какого перепуга объект должен знать о том, что его в принципе будет кто-то куда-то сохранять. Объект должен реализовывать только свою логику. Простой пример. У вас есть картотека с карточками. неужели кто-то вызывает метод карточки, после чего карточка магическим образом встает в шкаф на нужное место? Нет. Есть некий архивариус, который занимается этим.

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

Я присоединяюсь к вышесказанному, не пишите больше программ, а уж тем более статей как надо писать программы. И поумерьте свой пыл по поводу изобретения «лучшего в мире механизма работы с базой».
Про типовую реализацию синга — знаю. Она хуже чем моя.
Проблема в том, что ваша работает до первой пары конкурентных запросов.
А эти проблема мы решим когда нужно будет, а пока не стоит заморачиваться. Как там — излишнее перепроектирование !?
Thread safety для DAL — одна из первоочередных проблем, але. Потому что имеет прямое отношение к целостности данных.
По поводу объекта и сохранения — это глупость. Сохранение объекта — это должна быть его базовое поведение, если я его наследую от объекта «которые умеют сохраняться». И наличие «объектов умеющих сохраняться» — это основы основ ООП.
Почитайте хотя бы о DDD, если здравого смысла не хватает. Там специально выделяются «объекты которые умеют сохраняться». Хотя даже у Буча объекты разделялись на долгоживущие и короткоживущие.

Поэтому туфта не пройдет.
Пруфлинк на самосохраняющиеся объекты в ddd?
Так как минимум есть разделение на те которые сохраняются, и прочие. А то, что те которые сохраняются — должны делать это сами — это элементарно. Это их ответственность сохраняться, раз уж они могут. Разве так сложно усвоить, что в ООП объекты должны быть максимально самостоятельными, и выполнять то, что им свойственно. И да, есть объекты, которым свойственно жить дольше жизни работы ПО.
Еще раз — я хочу знать где вы это вычитали.
Big Blue Book
Эванс?

А вот насколько я помню, он то как раз и напирал на то, что объекты должны сохраняться только репозитариями.
Кстати, а множественное наследование в C# есть? Если вдруг нет, то что делать, если сущность, скажем, Admin надо унаследовать от User? Забить на естественное наследование в предметной области ради наследования от DBData?
Множественное наследование тут не нужно. Admin наследуете от User, а User от DBData
А User не должна храниться, или должна но не в БД.
Ну, как так может быть? Не придумывайте искусственные примеры.
User — это внешняя учетка, скажем, в соцсети, все данные там хранятся, а Admin — учётка соцсети, но с расширенными данными хранящимися на нашем хосте.
Ничего не изменится. Admin наследуете от User, а User от DBData.

Вы думаете, что это лишние? Проблемы тут не велика, мы создаем много объектов, и удаляем их воспользовавшись не всеми их методами. А тут вообще ни каких затрат.
Легко может быть, если User — абстрактный класс, не имеющий собственного хранилища.
И да попробуйте тоже самое сделать без reflection! Снова не сделаете!
Серьёзно, tac, послушайте что я вам говорю: не так давно набирали добровольцев, которые 500 дней жили в полной изоляции. Ну как «не так давно», весной 10го года. То есть эксперимент закончен уже, к сожалению. Но подготовка идёт дальше, а вы с вашей пунктуальностью, вашим вниманием к деталям и вашим знанием биологии будете действительно полезным в экспедиции на Марс.
И программирование вам для этого — ни к чему, tac, бросьте. Космос — вот что важно.
Смешной вы человек, на Марс я не хочу :)

А вы реально решите, а потом будем говорить, стоит ли вам заниматься программированием… а то может оказаться, что слишком поспешно делаете выводы, но вот программно реализовать у вас не получается.
Дело в том, дорогой мой друг tac, что программирование — это наука инженерная. В ней нет свободного полёта мысли. Простые решения, как правило, являются и правильными, а если что-то сложно, то это вредно и не нужно. Ваш код — он как правительство России, нельзя критиковать какого-то одного конкретного министра или депутата, нужно всю систему менять. Так ведь? А вы в ответ — «а как ещё так сделать», а не надо так делать вообще. Вообще не надо!
Но все эти ограничения, tac, слишком тесны, и в этом я с вами согласен. То ли дело в биологии. Огромные белковые макромолекулы, пространственные структуры, взаимодействие. Потрясающий в своём многообразии микромир, настоящая вселенная в пробирке. Ограниченный ум окажется заперт в космическом корабле во время перелёта, но вы, tac, несомненно найдёте достаточно важную научную проблему, чтобы скоротать время при перелёте. А программирование вам в этом всём никак не поможет. Ну его, tac, бросьте.
Да? Ну тогда обеспечьте мне, чтобы я вызывал

MyObject.Save() и он сохранялся бы в базе данных MongoDB — как угодно. Прошу, и тогда будем говорить, а пока один лишь гон. Вон lair хоть что-то сделал, только вот мне не нравится наследовать объекты как

public class Person : EntityBase<Person>


когда нужно

public class Person : EntityBase


Но у вас даже такой попытки нет. Так что — летите те как вы на Марс первым, уступаю.
Отчитав таким образом tac'а, гость осведомился:
— Профессия?
— Программист, — почему-то неохотно признался Иван.
Пришедший огорчился.
— Ох, как мне не везет! — воскликнул он, но тут же спохватился, извинился и спросил: — А как ваш аккаунт на хабре?
— tac.
— Эх, эх… — сказал гость, морщась.
— А вам, что же, мои программы не нравятся? — с любопытством спросил tac.
— Ужасно не нравятся.
— А вы какие читали?
— Никаких я ваших программ не читал! — нервно воскликнул посетитель.
— А как же вы говорите?
— Ну, что ж тут такого, — ответил гость, — как будто я других не читал? Впрочем… разве что чудо? Хорошо, я готов принять на веру. Хороши ваши программы, скажите сами?
— Чудовищны! — вдруг смело и откровенно произнес tac.
— Не пишите больше! — попросил пришедший умоляюще.
— Обещаю и клянусь! — торжественно произнес tac.

Ладно, чёрт с ним, с Марсом. Не хотите — не летите. Исследуйте рак, лечите Альцгеймера, секвенируйте ДНК или, на худой конец, оперируйте. Главное, tac, самому себе не лгите. Лгущий самому себе и собственную ложь свою слушающий до того доходит, что уж никакой правды ни в себе, ни кругом не различает, а стало быть входит в неуважение и к себе и к другим. Не уважая же никого, перестает любить, а чтобы, не имея любви, занять себя и развлечь, предается страстям и грубым сладостям, и доходит совсем до скотства в пороках своих, а все от беспрерывной лжи и людям и себе самому. Фу таким быть, tac!
Вот, блин, еще один комик, хоть и уважаемый :)
«Ну тогда обеспечьте мне, чтобы я вызывал MyObject.Save() и он сохранялся бы в базе данных MongoDB — как угодно»
Любой уважающий себя программист на этом месте должен вас послать нафиг. Потому что задачи в такой формулировке не ставятся — вы уже навязываете решение, вместо того, чтобы сформулировать задачу.

Правильная формулировка задачи: «сделайте удобный и прозрачный способ сохранения».

К удобству (со всех сторон) вашего способа есть множество претензий, неоднократно описанных в разного рода статьях. Можно начать хотя бы вот:
msdn.microsoft.com/en-us/magazine/dd882510.aspx#id0420053
Видимо, по второму кругу… объяснять устал.
Просто в ваших «объяснениях» нет ни капли аргументации, кроме вашего собственного удобства. А все ссылки на статьи других людей, в том числе и уважаемых в отрасли, вы игнорируете.
Все ваши ссылки — это ссылки на паттер Репозиторий. Других не было. Причем конкретную реализацию вы не показываете. Покажите какая именно реализация по вашему лучше?

Ну, а если удобство вызова для вызывающего — это не САМЫЙ ГЛАВНЫЙ аргумент, то я тогда не знаю что такое программирование вообще.
«Все ваши ссылки — это ссылки на паттер Репозиторий.»
То есть статью с msdn, которая вообще про другое, вы не прочитали.

«Покажите какая именно реализация по вашему лучше? „
_любая_ чистая реализация репозитория (Load, Load(query), Save, Delete) и POCO-объекты данных. Почему лучше? Потому что ответственности разнесены очевидным образом.
Если MyObject.Save() чем-то кому-то не удобен — значит он не хрена не смыслит в ООП. И это диагноз.
……………………………………..________
………………………………,.-‘"……………….``~.,
………………………..,.-«……………………………..»-.,
…………………….,/………………………………………..":,
…………………,?………………………………………………\,
………………./…………………………………………………..,}
……………../…………………………………………,:`^`..}
……………/……………………………………………,:"………/
…………..?…..__…………………………………..:`………../
…………./__.(….."~-,_…………………………,:`………./
………../(_…."~,_…….."~,_………………..,:`…….._/
……….{.._$;_……"=,_……."-,_…….,.-~-,},.~";/….}
………..((…..*~_……."=-._……";,,./`…./«…………../
…,,,___.\`~,……»~.,………………..`…..}…………../
…………(….`=-,,…….`……………………(……;_,,-"
………….\`~.*-,……………………………….|,./…..\,__
,,_……….}.>-._\……………………………..|…………..`=~-,
…..`=~-,_\_……`\,……………………………\
……………….`=~-,,.\,………………………….\
…………………………..`:,,………………………`\…………..__
……………………………….`=-,……………….,%`>--==``
…………………………………._\……….._,-%…….`\
……………………………..,<`.._|_,-&``…………….`\
И опять жжете.
Вас я посмотрю напугал код отображения, которого нельзя избежать. А вот то что код обращения к базе

var collection = db.GetCollection();
collection.Save(argObject);

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

Все ушел смотреть «побег 2» :)
lair, мы опять школьники и тролли ;)
Ну, так это же чистая правда, тролли из Викиреальности :)
К сожалению, не имею никакого отношения к викиреальности. Тутошние мы ;)
М-да? А поведение такое же :)
Я думаю, что вас везде встретят примерно одинаково.
Нет, это меня интересует весьма поверхностно, поскольку описанное вами мне кажется не слишком эффективным при работе с монго. Вот чем-то вроде riak'а — может быть.
Мне кажется, или перед нами новый Денис Попов? Или может быть это он и есть?
Неееее, Попов просто пхпшник. А тут все гораздо грустнее.

Вот он, наш герой — ru.vlab.wikia.com/wiki/Кабинет: Сергей_Яковлев
То-то думаю причем тут РНК и марс. Теперь все понятно.
Он к сожалению регулярно пишет. И поведение всегда одно и то же.
Гении, они такие. Никто их не понимает :)
Делаем абстрактный класс/интерфейс Storage с методами save, load, delete, find и т. п. Наследуем от него/реализуем его) конкретный класс конкретного хранилища (базы данных, файловой системы, мемкэша и т. д.). Если надо хоть 100 таких классов. Работаем так (псевдокод):
class Person {
  public int id;
  public string name;
}

Storage storage = new MongoDbStorage();

Person person = new Person("VolCh");
storage.save(person);
int person_id = person.id
delete person;

person = storage.load("Person", person_id);
persons = storage.find("Person", "name like Vol*");
storage.delete("Person", person_id);


Классы моделей (person) совершенно независимы от системы хранения и её интерфейсов. Замена системы хранения — замена одной строчки в коде storage = new ..., а то и в конфиге (плюс, если ещё не написан написать класс, инкапсулирующую конкретную систему хранения и предоставляющий интерфейс Storage). Конкретный класс-наследник Storage может быть как универсальным (рефлексия и т. п.), так и сильно заточенным под конкретные типы и хранилища.

Чем такая система не нравится?
… и вы тоже описываете паттерн Repository. Только еще и со строковыми запросами вместо QueryObject.
Я в курсе, просто автор очень не любит ссылки на паттерны и т. п. Решил привести наглядный пример, а вы всю малину испортили :(
Вы действительно не понимаете чем это хуже? Или стебетесь? Нет, если не понимаете я вам объясню… точно надо?
Действительно не понимаю. Код подобный этому я постоянно использую, даже без всяких навороченных ORM, но при желании заменить мои «велосипеды» на ORM/ODM это сделать будет просто, парой строчек может не обойдётся, на близко к тому.
Увы, для реляционной базы я тоже использую нечто такое. По привычке. Но объектная база на то и объектная, что может позволить немного большего.

Давайте разберем этот кусок.

Storage storage = new MongoDbStorage();

Person person = new Person("VolCh");
storage.save(person);


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

Т.е. переменная storage — локальна? Где-то за кадром у вас находится реальное обращение в базе, вы же не подключаетесь к ней каждый раз? Как это у вас происходит?
> Я так понимаю аналогичные кусочки у вас по всему коду. Т.е. логика такая вы создаете объект хранилища, затем нужный вам объект, и затем отправляете в хранилище. Так?
Для этого существует менеджер подключений, что-то вроде

Person person = new Person(«VolCh»);
Storage.instance.save(person);
Хотя мне и не нравится синглтон, но есть и другие варианты

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

Вот зачем писать «существует менеджер подключений», и тупить когда я говорю о TaskManager. Не нравится синглтон? Ну так зачем предлагать мой же вариант? Предложите эти ваши «другие варианты».

Ну а пул то тут причем? Т.е. вы реально каждый раз выполняете код типа

mongo = new Mongo();
mongo.Connect();
db = mongo.GetDatabase(DatabaseName);


и пусть за это думает пул, вам то до лампочки. Так? Где этот код у вас, в конструкторе Storage?
Да, и кстати, и Mongo есть пул подключений?
нет, я каждый раз не выполняю код типа указанного вами. и хранилищ у меня несколько. мало того, явных вызовов загрузки и сохранения у меня тоже нет. метод обработки бизнес-логики обрабатывает только бизнес логику. сохранение отдельно, загрузка отдельно, проверка входных данных тоже отдельно. зато я это легко и непринужденно тестирую.
Разговор шел про менеджер подключениЙ, а не менеджер подключениЯ. К тому же, если уж пошел разговор, то (а) название TaskManager не соответствует реализации и (б) вы что, реально запускаете сервер БД только когда приложение стартует?
> явных вызовов загрузки и сохранения у меня тоже нет

Да? И когда же данные попадают в базу?

> вы что, реально запускаете сервер БД только когда приложение стартует?
Я конечно извиняюсь но когда еще?
«Не нравится синглтон? [...] Предложите эти ваши «другие варианты».»
А их вам тоже уже предлагали, только вы не услышали. Dependency Injection и Service Locator.
Да, я часто использую «одно подключение на приложение». Пока проблем нет. Ну, не хотел бы уходить в сторону… вы и так не можете сконцентрироваться на предмете статьи, и мне тут отвечай за все админство и БД… :)
Вот про что мы вам и говорим, у себя используйте что хотите, но не надо советовать это делать другим и выставлять это как совершенное произведение инженерной мысли.
Я что-то не понял, где я говорил про «совершенное произведение инженерной мысли»?
Такой вариант чисто для демонстрации. storage инициализируется при запуске приложения, может быть глобальной переменной, может быть синглтоном, может статическим членом класса, может храниться в реестре приложения или DI-контейнере, может возвращаться фабрикой из пула, может просто передаваться параметром в конструкторы, сеттеры или методы по цепочке. В общем любым привычным способом передаётся в область видимости места использования. Аналогом вашего TaskManager.GetInstance().GetDatabase() тоже может.
> Классы моделей (person) совершенно независимы от системы хранения и её интерфейсов

Вы хотите неявно сказать, что в моей реализации классы сущностей (person) зависимы от системы хранения? Ведь нет же. Или поясните.
Напишите тест сохранения объекта не сохраняя сам объект в реальную базу
Дорогой вы меня утомили, и что мне делать — я разберусь сам. А если хотите говорить — говорите внятно. Зачем что-то писать, в чем проблема и т.д.
Я уже не говорю о том, что я противник всяких тестов, но снова речь то не об этом.
ей богу, вы смешны. умываю руки
«Я уже не говорю о том, что я противник всяких тестов»
Это как? Вы приложения заказчику отдаете без тестирования?

(впрочем, памятуя ваш код перцептронов, я не сильно удивлен)

Сильны, да. Тогда понятно, откуда у вас такой дизайн приложения.
StrategisData унаследован от DBData — это не зависимость? В «моей» схеме сущности предметной области с системой хранения вообще никак не связаны, её вообще может не быть. Хранением данных модели занимается приложение, а не сама модель. Модель существуют в вакууме можно сказать и занимается только моделированием предметной области. Откуда даггые берутся, куда потом передаются — это всё вне области её компетенции. Модель, если угодно, чистая функция без побочных эффектов. Подаст клиент на вход пользовательский ввод, записи из БД или файлов, захардкоженные данные или полученные из стороннего сервера по http — ей всё равно, при одинаковом входе будет одинаковый выход. А вход — только параметры конструкторов, сеттеров и методов. Как исключение — присваивание паблик свойств.

А DBData зависит от TaskManager.GetInstance.GetDatabase, который возвращает Database. Это не зависимость? При изменении Database на работу с например мускулом StrategisData не нужно перекомпилировать? (хотя может и не нужно, я не сильно в курсе).
> StrategisData унаследован от DBData

Это ровно такая же зависимость как у вас Person унаследован от object. Т.е. Это наследование как раз указывает на то, что эти объекты могут сохраняться. В этом нет ничего страшного.

> Хранением данных модели занимается приложение, а не сама модель

А вот это плохо. Я понимаю когда нет возможности. Но когда есть это плохо. Во-первых мне не нравится название «модель». В ООП нет такого понятия, и это привнесенное понятие. Есть понятие долгоживущего объекта, сущности если хотите. Т.е. я подозреваю, что у вас модель не имеет методов? Тогда это вообще не ООП. А если нет, чем так особен метод сохранения? Вам кажется сохранение это не бизнес-логика?

> А DBData зависит от TaskManager.GetInstance.GetDatabase, который возвращает Database. Это не зависимость?

Да, это зависимость, но она в одном конкретном месте — инкапсулирована в объект. Но какой именно объект наследник от Database будет возвращен системой зависит от логики окружения. Поэтому это слабая зависимость.

В отличии от сильной зависимости, когда вы делаете Storage storage = new MongoDbStorage();

И более того, эти зависимости у вас плодятся кучами ВСЮДУ где вы начинаете сохранять объекты. У вас сотни сильных зависимостей против одно слабой. Разницы не чувствуете, или вы не видите, что написав Storage storage = new MongoDbStorage(); вы создаете зависимость?

Это ровно такая же зависимость как у вас Person унаследован от object.

:) не ровно такая, как минимум теряю свободу в выборе свойств и методов. И object не может меняться волею разработчика, а DBData может.

Во-первых мне не нравится название «модель». В ООП нет такого понятия, и это привнесенное понятие.

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

Т.е. я подозреваю, что у вас модель не имеет методов?

Имеют, ещё как имеют. Процентов 80 кода это эти методы.

А если нет, чем так особен метод сохранения?

Он возлагает на класс вторую обязанность. Не только обрабатывать данные, но и заботится об их сохранении.

Вам кажется сохранение это не бизнес-логика?

Я в этом уверен, если мы не говорим о продуктах вроде IDE или админок к БД, где файлы или записи в БД являются объектами с которыми пользователь работает, а не средством хранения между сессиями. Возьмём например бухгалтерию. Там есть понятия «создать документ», «провести документ», «сторнировать документ», но нет понятия «сохранить документ в базу данных». «Сохранить» — это чисто логика приложения, как «печать» — логика представления.

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

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

Storage storage = new MongoDbStorage();

получаете доступ к объекту? От куда берется ваш storage в локальном месте?

(и вот это большая проблема, чем все то о чем вы ниже пишите)
Например App.GetInstance().GetStorage().Save(person)
мы все идиоты. поясните же нам наконец. а то все заладили «вы что, не понимаете, чем это хуже?»
Если в двух словах, то слишком много кода при работе с объектом Person. И это постоянно. Странно, что копи-пасту в моем классе в одном месте заметили, а дублирование работы с лишним объектом storage не земечаете.

Ведь все можно сделать прозрачно.

Ваш код надо переписать так:

class Person {
public int id;
public string name;
}

Person person = new Person("VolCh");
person.save();
int person_id = person.id
person.Delete();

person = person.load(person_id);
persons = person.LoadAll("name like Vol*");
person.delete(person_id);


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

person.delete(person_id);

на

person.delete();
а выше, где person.Delete(); ей соответствует нотация на С++

delete person;

я спутал — надо заменить на person=null;
Что касается persons = person.LoadAll(«name like Vol*»);

тоже возможны варианты, лучше будет

persons = new Person().LoadAll(«name like Vol*»);
Вы действительно считаете, что персоны = новая Персона.ЗагрузиВсе(имя как Vol*) лучше чем персоны = Хранилище.найди(имя как Vol*)?
Да. Хотя в известном смысле это натяжка. Но это лучше, чем получать ссылку на хранилище, для осуществления работы. Возможно, тут как и у Фаулера, лучше применить статический метод Найди — для данного метода это дискуссионно.
Вы получаете ссылку на базу данных, я на хранилище — разница?
Откуда слишком много кода? Соответствие построчное. storage замена вашему наследованию от DBData (про которое вы забыли в этом примере :) ). Насчёт естественнее — давайте на русским переведём.
Мой вариант:

персона = новая Персона(VolCh)
хранилище.сохрани(персона)
персона = хранилище.загрузи(Персона №)
персоны = хранилище.найди(Персона имя как Vol*)
хранилище.удали(Персона №)

Ваш:

персона = новая Персона(VolCh)
персона.сохрани()
персона = персона.загрузи()
персоны = персона.найди_все(имя как Vol*)
персона.удали

Что сохрани, что загрузи, что найди, что удали?! Как по мне, то мой естественнее, даже если не учитывать, что я названия методов (в частности load) подбирал под ваш DBData. У вас названия методов не согласуются с именем объекта. Кто ищет все? Персона?
Особенно логичен вот этот вот код:

person = person.load(person_id);

(Уже созданный объект внезапно возвращает другой объект. А что произошло с состоянием первого объекта?)

Ну и да:
«Person person = new Person(»VolCh");"
Как мне понять, что этот объект еще не получил данные из БД? Что вернет его свойство int Age до загрузки из БД?
Что-то у вас критика мало конструктивная. Предлагаю так. Покажите мне две конкретных реализации паттернов Репозиторий и ActiveRecords, и я тогда смогу тогда сравнить и объяснить, чем моя реализация лучше обеих вместе взятых :) Ну, а так по идеи — это конечно компиляция эти паттернов, но это на уровне идей, а на практике вон — видим как бывает.
Только не просто каких-то реализаций паттернов, а тех за качество которых вы отвечаете, и не будите менять по ходу рассмотрения.
Впрочем если смотреть по Фаулеру — то мной действительно предложена реализация ActiveRecords, но т.к. я против static — то методы не статические, и в отличии от примеров Фаулера, я работаю не с реляционной базой. А идея да, та же самая.
Но это так естественно, что изучать это как паттерн — я никогда не изучал. Но вот странно, что люди возражают против использования этого паттерна…
Не зря у Фаулера самым мощным считается паттерн DataMapper.
Это уже лирика, зачем мне что-то более мощное в более простом случае?
pastebin.com/7t6f0fNE

Вот вам конкретная реализация. Это не production-код (специально подчеркиваю), это я на коленке набросал за 20 минут прямо сейчас. Соответственно, реализация БД — абстрактная, по образцу современных ORM.
1. Это конкретная реализация чего?

2. В ней та же самая проблема, что у VolCh. Нужно или создавать объект репозитория в 100 местах (у вас в конструкторе каждого объекта, где используется сохранение), или так или иначе получить ссылку на репозиторий (у вас это не показано как). Это настолько большая проблема, что все остальные мелочи можно не рассматривать (хотя наследование от обобщенного класса (class Person: IIdentifiableObject) — это мощно и незачем!)

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

Тьфу, перепутал, вы не создаете объект репозитория в 100 местах, вы получаете к нему доступ в 100 местах. Но проблема не меняется.
Ну, и так на всякий случай — чего это у вас все классы, которые использую сохранения в качестве параметра конструктора принимают хранилище? И это вы называете не глупостью? Это так все бизнес-классы не занимаются ничем другим, кроме бизнес-логики — хотя каждый из них вынужден для своего создания принимать объект системного класса — хранилище. И это нормальная архитектура?, тьфу.
tac, вы реально жжете. Какого хрена вы нам тут вещаете, если не знаете даже что такое Inversion of Control?
Потому что видимо это хрень по его мнению, а хрень ему недосуг изучать.
Меня просто поражает, как этот человек считает всех вокруг идиотами и сам же расписывается в собственном незнании. Его даже троллить не надо, он сам себя троллит.
Видите ли, есть программисты, и начинающие. Если вы чего-то не понимаете из того, что я вам объясняю — вы начинающий — и обижаться тут не стоит. Но Вы не идиот — вы воинствующий невежда. Вы бы хоть в попад отвечали бы, а то все мимо и не конкретно.
tac, к вашему глубочайшему сожалению, мой (и ваш) профессиональный уровень оцениваете не вы, а рынок. Грубо говоря, приезжайте в Москву и попробуйте устроится на работу. Будете зарабатывать больше, чем я — тогда и будете говорить мне, кто тут невежда.