
[spoiler] В отличие от динамического PHP, большинство языков под .NET CLR 2.0 - языки со статической типизацией. Статическая типизация вкупе с метаданными типов .NET позволяет организовать хорошую поддержку Code Completion (IntelliSense) в среде разработки, проверку ошибок на этапе компиляции и т.д. Чтобы использовать эти преимущества необходим другой подход к сущностям.
Немного истории
Сначала были просто классы и классы-менеджеры, т.е. каждая сущность, например, "Пользователь" представляла собой класс, ее олицетворяющий - BXUser с полями и свойствами, а к нему в нагрузку шел глобальный BXUserManager - у которого были методы по сохранению, обновлению, выборке - все методы содержали прямые вызовы SQL запросов и для каждой новой сущности приходилось писать все с нуля.
Поэтому в недрах отдела зародилась идея сделать единый механизм выполнения типовых запросов к БД, чтобы разработчик тратил меньше время на декларацию сущностей, а пользователь API мог комфортно работать, используя стандартизованные методы и использовать в полной мере IntelliSense в Visual Studio.
Первая версия ORM просуществовала до последнего обновления 3-ей версии, и обкатывалась на инфоблоках. Существенные изменения и дополнения механизм претерпел к версии 4 - теперь это полноценная ORM поверх слоя базы данных.
Что собственно есть
API сущностей решает 2 задачи:
- Отображает слой базы данных на уровень .NET кода
- Автоматизирует решение типовых задач: сохранение сущностей, выборки, фильтрация


есть свойства Author, AuthorBlog, AuthorBlogId, AuthorName, ... , причем свойство Author имеет тип BXBlogUser и тоже в свою очередь имеет разные свойства. Кстати, значение свойства AuthorName автоматически вычисляется при указания идентификатора пользователя через AuthorId

но может быть и задано вручную. Удобно, не правда ли?
Заглядывая внутрь BXBlogComment можно найти метод GetList. Этот могущественный метод - основа всей ORM, который опирается на механизм фильтрации и выборки, но об этом чуть позже. Рассмотрим типовой пример из модуля "Блоги":
BXBlogCommentCollection commentCollection #008000;">= BXBlogComment.#0000FF;">GetList#000000;">( #008000;">new BXFilter#000000;">( #008000;">new BXFilterItem#000000;">(BXBlogComment.#0000FF;">Fields.#0000FF;">Post.#0000FF;">Id, BXSqlFilterOperators.#0000FF;">Equal, #FF0000;">256#000000;">), #008000;">new BXFilterItem#000000;">(BXBlogComment.#0000FF;">Fields.#0000FF;">LiveRootNodeIndex, BXSqlFilterOperators.#0000FF;">Greater, #FF0000;">3#000000;">), #008000;">new BXFilterItem#000000;">(BXBlogComment.#0000FF;">Fields.#0000FF;">LiveRootNodeIndex, BXSqlFilterOperators.#0000FF;">LessOrEqual, #FF0000;">7#000000;">) #000000;">), #008000;">new BXOrderBy#000000;">(#008000;">new BXOrderByPair#000000;">(BXBlogComment.#0000FF;">Fields.#0000FF;">LeftMargin, BXOrderByDirection.#0000FF;">Asc#000000;">)#000000;">), #008000;">new BXSelectAdd#000000;">( BXBlogComment.#0000FF;">Fields.#0000FF;">AuthorBlog.#0000FF;">Id, BXBlogComment.#0000FF;">Fields.#0000FF;">AuthorBlog.#0000FF;">Slug, BXBlogComment.#0000FF;">Fields.#0000FF;">AuthorBlog.#0000FF;">Categories.#0000FF;">Category.#0000FF;">Sites.#0000FF;">SiteId, BXBlogComment.#0000FF;">Fields.#0000FF;">Author, BXBlogComment.#0000FF;">Fields.#0000FF;">Author.#0000FF;">User, BXBlogComment.#0000FF;">Fields.#0000FF;">Author.#0000FF;">User.#0000FF;">Image #000000;">), #0600FF;">null #000000;">)#008000;">;
здесь мы выбираем комментарии для поста в блоге в определенном интервале и сортируем их в нужном порядке.
Первым аргументом GetList'а идет фильтр. Фильтры бывают разными. Атомарным фильтром является является фильтр "поле равно значение" - он олицетворен классом BXFilterItem. Еще есть фильтр AND (BXFilter) и OR (BXFilterOr). В этом примере мы выбираем те комментарии, у которых LiveRootNodeIndex лежит в диапазоне от 4 до 7, а идентификатор связанной сущности BXBlogPost равен 256.
Второй аргумент - это порядок сортировки - тут все просто, задаем парами вида поле-направление.
Третий аргумент - еще одна мощная вещь - это выборка. При помощи BXSelect можно указать список полей, которые нужно выбрать в запросе, а также полей связанных сущностей (API автоматически сделает нужные JOIN'ы). Таким образом мы, например, для каждого комментария можем выбрать дополнительно нужную информацию о его авторе и о блоге автора. Все это будет сделано в рамках одного запроса. Конечно, мы можем не указывать дополнительных сущностей в выборке - к ним все равно можно будет обратиться через свойства комментария, но уже с дополнительным запросом по требованию. В нашем случае мы дополнительно выбираем (BXSelectAdd означает, что перечисленные поля и сущности будут выбраны дополнительно к основной) 2 поля из связанной сущности AuthorBlog (блог автора) - идентификатор и адрес, и список идентификаторов сайтов, к которым принадлежат категории, к которым принадлежит все тот же блог автора комментария. Т.е. API позволяет нам сделать глубокую выборку связанных данных. Даже множественных. Ну и в добавок нам понадобится информация об авторе комментария и его аватар - это уже конкретные сущности (BXBlogUser, BXUser, BXFile) - они будут выбраны полностью, со всеми полями.
Обратите внимание, что поля сущности задаются не строками, а свойствами объекта BXBlogComment.Fields - схемы сущности. Схема сущности описывается отдельным классом и содержит поля сущности и их привязку к базе данных. Поля могут описывать простые свойства, имеющие прямые прототипы в базе данных (BXTableField), выражения, чья логика отображения задается кодом (BXCalculatedField), одиночные и множественные связки с другими сущностями (BXSchemeField, BXLinkedField).
Таким образом общение с сущностями происходит на уровне .NET кода, а не SQL - тем самым минимизируется вероятность ошибки, ведь корректность синтаксиса теперь за нас проверяет компилятор, а отсутствие прямых вставок SQL-кода защищает нас от различных дыр в безопасности.
Как с этим работать
В ходе разработки сайта или модуля разработчикам, возможно, придется столкнуться со следующими задачами:
- Описание сущностей
- Использование сущностей
Класс схемы сущности наследуется от BXScheme и содержит в своем статическом конструкторе (на данный момент это особенность API) описания полей, а также предоставляет доступ к полям через свойства. Например, схема Вашей сущности может выглядеть следующим образом:
#0600FF;">public #FF0000;">class CustomerScheme #008000;">: BXScheme#008000;"><CustomerScheme#008000;">> #000000;">{ #0600FF;">static CustomerScheme#000000;">(#000000;">) #000000;">{ SetTable#000000;">(#666666;">"customer", #666666;">"cm"#000000;">)#008000;">; AddField#000000;">(#666666;">"Id", #008000;">new BXTableField#000000;">(#666666;">"id", SqlDbType.#FF0000;">Int, #FF0000;">0, #0600FF;">false, #0600FF;">true, #0600FF;">true#000000;">)#000000;">)#008000;">; AddField#000000;">(#666666;">"Name", #008000;">new BXTableField#000000;">(#666666;">"name", SqlDbType.#0000FF;">NVarChar, #FF0000;">256, #0600FF;">false, #666666;">""#000000;">)#000000;">)#008000;">; AddField#000000;">(#666666;">"Birthday", #008000;">new BXTableField#000000;">(#666666;">"birthday", SqlDbType.#0000FF;">DateTime, #0600FF;">true, x #008000;">=> DateTime.#0000FF;">Now#000000;">)#000000;">)#008000;">; #000000;">} #0600FF;">public BXSchemeFieldBase Id #000000;">{ get #000000;">{ #0600FF;">return GetField#000000;">(#666666;">"Id"#000000;">)#008000;">; #000000;">} #000000;">} #0600FF;">public BXSchemeFieldBase Name #000000;">{ get #000000;">{ #0600FF;">return GetField#000000;">(#666666;">"Name"#000000;">)#008000;">; #000000;">} #000000;">} #0600FF;">public BXSchemeFieldBase Birthday #000000;">{ get #000000;">{ #0600FF;">return GetField#000000;">(#666666;">"Birthday"#000000;">)#008000;">; #000000;">} #000000;">} #000000;">}
Класс сущности наследуется от BXEntity и содержит в себе описание свойств сущности и пользовательскую бизнес-логику. В самом простом случае это будет выглядеть так:
#0600FF;">public #FF0000;">class Customer #008000;">: BXEntity#008000;"><Customer, CustomerCollection, CustomerScheme#008000;">> #000000;">{ #0600FF;">public #0600FF;">override #FF0000;">string EntityId #000000;">{ get #000000;">{ #0600FF;">return #666666;">"Customer"#008000;">; #000000;">} #000000;">} #0600FF;">public #0600FF;">override #FF0000;">string ModuleId #000000;">{ get #000000;">{ #0600FF;">return #666666;">"MyModule"#008000;">; #000000;">} #000000;">} #0600FF;">public #FF0000;">int Id #000000;">{ get #000000;">{ #0600FF;">return GetValue#008000;"><#FF0000;">int#008000;">>#000000;">(#666666;">"Id"#000000;">)#008000;">; #000000;">} #000000;">} #0600FF;">public #FF0000;">string Name #000000;">{ get #000000;">{ #0600FF;">return GetValue#008000;"><#FF0000;">string#008000;">>#000000;">(#666666;">"Name"#000000;">) #008000;">?? #666666;">""#008000;">; #000000;">} set #000000;">{ SetValue#000000;">(#666666;">"Name", value #008000;">?? #666666;">""#000000;">)#008000;">; #000000;">} #000000;">} #0600FF;">public DateTime Birthday #000000;">{ get #000000;">{ #0600FF;">return GetValue#008000;"><DateTime#008000;">>#000000;">(#666666;">"Birthday"#000000;">)#008000;">; #000000;">} set #000000;">{ SetValue#000000;">(#666666;">"Birthday", value #008000;">!= DateTime.#0000FF;">MinValue #008000;">? #000000;">(#FF0000;">object#000000;">)value #008000;">: #0600FF;">null#000000;">)#008000;">; #000000;">} #000000;">} #000000;">}
Класс коллекции - на данном этапе это просто обертка и описывается одной строкой:
#0600FF;">public #FF0000;">class CustomerCollection #008000;">: BXEntityCollection#008000;"><Customer, CustomerCollection, CustomerScheme#008000;">> #000000;">{#000000;">}
Таким образом для типовых сущностей задача описания носит действительно описательный характер. А с написанием кода может справиться, например, какой-нибудь кодогенератор.

Для более сложной логики класс сущности содержит набор виртуальных и шаблонных методов, которые можно переопределить в зависимости от сложности встраиваемого поведения.
Для критичных операций можно написать методы с прямыми SQL запросами - API сущностей позволяет создать сущность из голой SQL выборки SqlDataReader'ом.
В конечном итоге мы получаем унифицированный механизм для доступа к данным - все сущности имеют метод GetList, набор полей и типизированные свойства. Т.е. нам не придется задумываться, а как выбрать тот или иной объект по такому-то условию - методы выборки одинаковы для всех сущностей, а Visual Studio подскажет, какие поля или свойства есть у сущности.
Заключение
API сущностей - это уже сложившийся механизм, который прошел "боевое крещение" инфоблоками, форумами и блогами, и призван избавить разработчика от необходимости писать рутинные SQL запросы для типовых задач, задействовать особенности статической типизации и использовать удобные средства, которые нам предлагает IDE.
Поддержку LINQ можно реализовать, например, как стороннее решение.
Если честно, то метод BXBlogComment.GetList выглядит ужасно
Про ASP.NET MVC спрашивать не буду. Разработчикам нравится работать с передовыми технологиями, которые обеспечивают более высокую продуктивность. Надеюсь, что скоро БУС.NET догонит и перегонит мир ASP.NET