.NET ORM нужны виртуальные и не могут работать с запечатанными?

Я только начинаю работать с .NET ORM, до такой степени, что еще даже не выбрал между Entity Framework и NHibernate. Но в обоих случаях я сталкиваюсь с проблемой, заключающейся в том, что они, похоже, хотят, чтобы я различными способами скомпрометировал целостность моей модели предметной области, особенно в тонкостях проектирования объектов C#. Это один из нескольких вопросов по теме.


Существует причина, по которой virtual не используется по умолчанию для методов C#. Объекты в моей модели предметной области не готовы давать обещания о поведении подклассов, за исключением очень специфических случаев, когда я помечаю их как таковые. Иными словами, для очень немногих методов объектов моего домена уместно добавить хук для неопределенной новой функциональности.

Тем не менее, NHibernate хочет, чтобы я сделал все virtual, а Entity Framework хочет, чтобы я сделал все ссылки на сущности virtual. Я понимаю, зачем им это нужно (для создания прокси-объектов), и я понимаю, что на самом деле это законное использование наследования и virtual --- они на самом деле подключаются к моим свойствам, чтобы добавить новую функциональность. Но меня раздражает, что я должен аннотировать свои классы модели предметной области чем-то, что полностью связано с постоянством, а вовсе не с выражением их фактического контракта с реализаторами и потребителями.

В качестве небольшой проблемы, с которой я, как я понимаю, ничего не могу поделать, часто бывает выразительно аннотировать мои классы с помощью sealed по всем обычным причинам. Однако это немного менее неприятно, поскольку исключение аннотации из моих объектов домена с целью сохранения кажется менее плохим, чем ее добавление.


Разочаровывает то, что после нескольких лет чтения таких книг, как Effective C#, или блогов, таких как блоги Эрика Липперта, которые дают отличные советы о том, как проектировать выразительные и пуленепробиваемые объекты C#, необходимость использовать ORM заставляет меня бросать большая часть этих знаний вылетела из окна. Я надеюсь, что кто-то здесь может указать, где я ошибаюсь, либо в моем понимании их возможностей, либо в моих мыслях о моделировании предметной области и роли ORM.


person Domenic    schedule 19.03.2011    source источник
comment
Я смотрю на код совершенно противоположным образом — я хочу, чтобы virtual был по умолчанию (для методов), и я могу сосчитать, сколько раз у меня был sealed класс на пальцах: Если вы создадите подкласс и что-то сломаете, это ваша вина< /i> :-) Я столкнулся со слишком большим количеством негибкого кода (из других библиотек), который я не могу хорошо использовать и не могу изменить.   -  person    schedule 19.03.2011
comment
У меня прямо противоположное убеждение в использовании виртуального — используйте его как можно больше, если только вы не определили что-то, что действительно должно оставаться как есть. Также я бы не поверил никому из MS, защищая решение не использовать виртуальные методы по умолчанию. Вы можете просто просмотреть код из различных API-интерфейсов .NET, предоставляемых MS, включая ASP.NET, EF, WCF, WF и т. д., и вы обнаружите, что вместо правильного дизайна и расширяемой архитектуры они предоставляют ограниченную расширяемость или отсутствие расширяемости, скрытую за закрытыми внутренними , не виртуальные и статические функции. Также кстати. virtual часто требуется для имитации фреймворков.   -  person Ladislav Mrnka    schedule 20.03.2011
comment
Мое первое правило в программировании — не быть пуристом и использовать свой мозг, прежде чем писать код. У всех API и фреймворков всегда есть плюсы и минусы, и ваша задача — найти наилучший компромисс.   -  person Ladislav Mrnka    schedule 20.03.2011
comment
@Ladislav: но... У меня новый проект с нуля! Мой внутренний пурист хочет выйти и порезвиться; он устал от того, что его отталкивают заботы о заброшенных месторождениях;)   -  person Domenic    schedule 20.03.2011
comment
@Domenic: я не могу не согласиться. Я также устал от разработки, которую я должен делать на работе, но просто вы должны понимать, что структура постоянства обеспечивает некоторую магию позади, но им нужны крючки, чтобы иметь возможность выполнять эту магию. Это компромисс.   -  person Ladislav Mrnka    schedule 20.03.2011
comment
@pst: альтернативный взгляд на то, что если вы создаете подкласс и ломаете что-то, что является вашей ошибкой, заключается в том, что если вы создаете дыру в безопасности в своем суперклассе, которую враждебный подкласс может использовать для атаки на ваших пользователей, это вина парня, пишущего суперкласс.   -  person Eric Lippert    schedule 20.03.2011


Ответы (5)


Это касается не только ORM .NET — те же ограничения применимы и к ORM Java.

Хотя в Java все виртуально, если вы явно не заявите об обратном, поэтому необходимость удовлетворения ORM очень похожа на ситуацию, которую вы обнаружите с sealed:

исключение аннотации из объектов моего домена с целью сохранения кажется менее плохим, чем ее добавление.

Это сводится к следующему: игнорирование стойкости — стоящая цель, но она не может быть достигнута на 100 %, если вы не готовы также игнорировать мелкие детали, такие как загрузка памяти и производительность. тоже.

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

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

person Bevan    schedule 19.03.2011
comment
В сочетании с тем, что Mystere Man использует фреймворки, это всегда означает компромисс, ваше замечание о том, что настойчивость — это дырявая абстракция, было именно тем углом, который мне был нужен, чтобы обойти эти трудности. Конкретные моменты, касающиеся нагрузки на память и производительности, также были отличными. - person Domenic; 20.03.2011

Как бы это помягче выразиться... Извините, не могу. Преодолейте это.

Я согласен с вами на 100%, но использование фреймворков всегда означает компромисс. Не хотите идти на компромисс? Построй его сам. Вот и все.

Чтобы быть немного менее антагонистичным, есть решение вашей проблемы, и это использовать что-то вроде automapper для перевода между вашей дырявой подсистемой постоянства и остальной частью вашего приложения. По сути, вы поддерживаете свою модель предметной области в чистоте и порядке и проектируете именно так, как вам нравится, а затем используете слой перевода для сопоставления между ней и вашим неприятным, уродливым ORM.

Но, это действительно много работы. И за небольшое количество чистоты, от которого вы отказываетесь, вы экономите много усилий.

person Erik Funkenbusch    schedule 19.03.2011
comment
Это действительно было очень полезно. Мне особенно нравится пункт о том, что использование фреймворков всегда означает компромисс, что дает мне, я думаю, правильное направление для взгляда на это — ни один фреймворк не является волшебным. И совет про automapper тоже отличный, хотя, как вы говорите, наверное, не лучшее место для времяпрепровождения --- т.е. это, вероятно, было бы достойно того, чтобы Айенде украл обвинительное заключение вашего клиента. - person Domenic; 20.03.2011
comment
Хотел бы я принять два ответа ... просто хотел, чтобы вы знали, что я был очень близок к тому, чтобы принять ваш :) - person Domenic; 20.03.2011
comment
@Domenic - Все в порядке, ответ Бевана меня тоже очень порадовал;) - person Erik Funkenbusch; 20.03.2011

Есть причина, по которой виртуальный не используется по умолчанию для методов в C# [ссылка на интервью с Андерсом Хейлсбергом].

Хейлсберг на самом деле говорит о дизайне фреймворка API. Он ничего не говорит о бизнес-приложениях. Поэтому его правила меньше применяются в LOB-приложениях. Поскольку вы используете O/RM, вы, вероятно, пишете LOB-приложение.

часто бывает выразительно аннотировать мои занятия запечатанными по всем обычным причинам [ссылка на блог Эрика Липперта].

Вы ссылаетесь на одну из статей Эрика Липперта, который написал эту статью в контексте своей работы в команде компилятора C#. Общие Руководство по проектированию фреймворка на самом деле содержат противоположное правило:

НЕ запечатывайте классы без веской причины. [пункт 6.3]

Другими словами, то, что говорит Эрик Липперт, не является общим правилом.

Лично я, когда пишу LOB-приложения, фактически запечатываю свои классы и пишу невиртуальные методы, когда это возможно. Однако это не имеет ничего общего с внесением критических изменений в более позднюю версию, потому что это почти исключительно проблема проектирования фреймворка.

Нет, я делаю это, потому что мне легче делать предположения о моем коде. Другими словами: это делает мой код более удобным для сопровождения.

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

Очевидно, вам тоже нужна эта гибкость, и, поскольку вы пишете LOB-приложение, просто будьте практичны и помните, что:

В любом случае, они больше похожи на рекомендации

person Steven    schedule 19.03.2011
comment
Это было очень полезное уточнение; Теперь я вижу, что извлек правильные уроки из постов в блогах, на которые я ссылался, неправильно истолковав их контекст. Как вы сказали, на самом деле речь идет об упрощении предположений и, таким образом, поддержке кода. Но тот факт, что распечатывание и виртуализация по мере необходимости не так уж и плохи, имеет большой смысл. - person Domenic; 20.03.2011
comment
Ну, я не согласен с руководящими принципами здесь. Я думаю, что лучшим руководством будет сказать, что все должно быть запечатано, если нет веской причины сделать это распечатанным. Незапечатанный – это функция. Особенности имеют стоимость. Вы должны быть готовы оплатить эти расходы, если решите реализовать эту функцию. - person Eric Lippert; 20.03.2011
comment
@Eric: Возможно, вам следует обсудить это с Кшиштофом и Брэдом. Вы явно не на том же пути здесь :-). Возможно, разница здесь заключается в том, что Брэд описывает как основное различие между фреймворком и библиотекой. Вы пишете библиотеку, в то время как фреймворки, как правило, более открыты для настройки. Позвольте мне добавить еще одну цитату из FDG: частью проектирования расширяемости является знание того, когда ее ограничить, и запечатанные типы являются одним из механизмов, с помощью которых это можно сделать. Аминь - person Steven; 21.03.2011

Я понимаю разочарование. Одна из возможностей — использование фреймворка аспектно-ориентированного программирования (АОП), такого как PostSharp, для пометки всех свойств virtual во время компиляции. Обратной стороной этого являются накладные расходы, связанные с процессом плетения PS, что увеличивает общее время компиляции.

Просто для удовольствия: я на самом деле сейчас работаю над исследовательским проектом, который представляет собой предварительную ORM на основе АОП (предварительно называемую Trinity). Его цель — обеспечить полную мощность для ленивой загрузки без необходимости использования прокси (или ключевого слова virtual). Самое главное, он позволяет моделям сохранять независимость (без участия наследования, объектов POCO и т. д.), но предоставляет те же возможности, что и что-то вроде NHibernate.

АОП все еще находится на исследовательском уровне, но работать над этим проектом было интересно. Я попытаюсь открыть исходный код проекта, как только документ будет готов.

person TheCloudlessSky    schedule 19.03.2011
comment
Мне интересны ваши идеи, и я хотел бы подписаться на вашу рассылку! Нет, серьезно, как я могу держать себя в курсе этого? - person Domenic; 20.03.2011
comment
@Domenic - Спасибо за интерес к проекту :). Это технический отчет/пилотное исследование для моей степени, и в настоящее время есть шанс, что он будет опубликован, потому что это новая работа. Я постараюсь получить более подробную информацию об этом в ближайшее время - и я постараюсь сообщить вам :). - person TheCloudlessSky; 20.03.2011

Не нужно делать все virtual в NHibernate. Если вы не используете «динамические прокси», вам не нужно делать все виртуальным, и вы можете сделать свои классы запечатанными.

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

Вы можете отключить функцию динамического прокси, указав lazy=false в сопоставлении классов:

<class name="MyEntity" table="SomeTable" lazy="false">
</class>
person Frederik Gheysels    schedule 19.03.2011
comment
Хмм хорошо. Насколько я понимаю, это плохая практика: ayende.com/Blog/archive/2010/08/04/ - person Domenic; 20.03.2011
comment
Думаю, я хочу сказать, что мечтаю о волшебном сказочном мире, в котором я мог бы получить ленивую загрузку без динамического проксирования. (Возможно, переписывание IL после сборки в стиле PostSharp? Что за кроличья нора...) - person Domenic; 20.03.2011
comment
Postsharp фактически сделает вашу библиотеку зависимой. В таком случае вам вообще не нужно использовать POCO и здороваться с базовым классом EntityObject в EF. - person Ladislav Mrnka; 20.03.2011
comment
@Ladislav - Как использование PS может сделать ваше приложение зависимым от сохраняемости? Вы можете легко использовать инфраструктуру АОП, такую ​​как PostSharp, чтобы добавить ключевое слово virtual ко всем вашим свойствам в модели. - person TheCloudlessSky; 20.03.2011
comment
@Domenic: пример кода в примере Айенде немного более радикален. Там тоже все ассоциации ленивы. Однако указание 'lazy=true' для класса не влияет на ленивость ассоциаций. Я не думаю, что отключение динамических прокси — плохая практика. Я никогда не использовал динамические прокси с nhibernate и до сих пор не испытывал с ним никаких проблем. (Хотя я использую ленивую загрузку коллекций/ассоциаций). - person Frederik Gheysels; 20.03.2011
comment
@TheCloudlessSky: Если вы используете АОП для добавления виртуального ключевого слова ко всем своим свойствам, то нет никакой разницы, делать ли это сразу в коде, не так ли? Если вы используете АОП для добавления отложенной загрузки к своим свойствам навигации, вы привяжете свою скомпилированную библиотеку к используемой структуре постоянства, потому что для этой отложенной загрузки потребуется либо контекст из EF, либо сеанс из NHibernate и т. д. - person Ladislav Mrnka; 20.03.2011
comment
Конечно, это тот же скомпилированный код. Но это другое, потому что конкретный аспект сохраняемости является отдельной проблемой, и поэтому не должен просачиваться в код. Весь смысл АОП состоит в том, чтобы разделить эти проблемы (модель и постоянство) в написанном коде, а затем объединить их во время выполнения/компиляции. Проверьте мой пост для более подробной информации. - person TheCloudlessSky; 20.03.2011