1С-Битрикс: Управление сайтомНа главную страницу
Клиентам
Маркетплейс
Партнерам
Разработчикам
Интеграция с 1С
Идея?


Личный кабинет
Авторизоваться
Регистрация
(войти) Корзина
Логин:

Пароль:



Забыли свой пароль?
Регистрация
Войти как пользователь:
Войти как пользователь
Вы можете войти на сайт, если вы зарегистрированы на одном из этих сервисов:
ВКонтакте
Мой Мир
Twitter
Facebook
Google
Livejournal
Яндекс
Rambler
Mail.Ru
Liveinternet
Blogger
OpenID
Используйте вашу учетную запись VKontakte.ru для входа на сайт.
Используйте вашу учетную запись Мой Мир@Mail.ru для входа на сайт.
Используйте вашу учетную запись на Twitter.com для входа на сайт.
Используйте вашу учетную запись на Facebook.com для входа на сайт.
Используйте вашу учетную запись Google для входа на сайт.
.livejournal.com
@yandex.ru
@rambler.ru
@mail.ru
http://www.liveinternet.ru/users/ /
.blogspot.com
OpenID:
  • Документация
    • Платформа PHP
    • Корпоративный портал
    • Платформа ASP.NET
    • Отраслевые решения
    • Marketplace
    • Аренда приложений (SaaS)
  • Обучение и сертификация
    • Онлайн-курсы и сертификация
    • Учебные центры
    • Мое обучение
    • Учебные видеоролики
  • Центр поддержки
    • Поддержка
    • FAQ
    • Мои обращения
  • Сообщество
    • Блоги Битрикс
    • Блоги веб-разработчиков
    • Общие форумы
    • Веб-разработчики
      • Моя страница
      • Мои сообщения
      • Группы
      • Найти коллег
  • Cтатьи
    • Архив
Главная / Общение / Блоги / dev.NET

Про ORM

  • Блоги
  •  » 
  • dev.NET

Про ORM

0
Dmitry Pyalov [Дмитрий Пялов]
26.05.200913:0926.05.2009 13:09:15
Сегодня я расскажу про такую составляющую системы "1С-Битрикс: Управление сайтом ASP.NET", как сущности. Этот механизм - один из основополагающих механизмов системы, претерпел значительные изменения к 4-й версии.

В отличие от динамического PHP, большинство языков под .NET CLR 2.0 - языки со статической типизацией. Статическая типизация вкупе с метаданными типов .NET позволяет организовать хорошую поддержку Code Completion (IntelliSense) в среде разработки, проверку ошибок на этапе компиляции и т.д. Чтобы использовать эти преимущества необходим другой подход к сущностям.

Немного истории

Сначала были просто классы и классы-менеджеры, т.е. каждая сущность, например, "Пользователь" представляла собой класс, ее олицетворяющий - BXUser с полями и свойствами, а к нему в нагрузку шел глобальный BXUserManager - у которого были методы по сохранению, обновлению, выборке - все методы содержали прямые вызовы SQL запросов и для каждой новой сущности приходилось писать все с нуля.

Поэтому в недрах отдела зародилась идея сделать единый механизм выполнения типовых запросов к БД, чтобы разработчик тратил меньше время на декларацию сущностей, а пользователь API мог комфортно работать, используя стандартизованные методы и использовать в полной мере IntelliSense в Visual Studio.

Первая версия ORM просуществовала до последнего обновления 3-ей версии, и обкатывалась на инфоблоках. Существенные изменения и дополнения механизм претерпел к версии 4 - теперь это полноценная ORM поверх слоя базы данных.

Что собственно есть

API сущностей решает 2 задачи:
  • Отображает слой базы данных на уровень .NET кода
  • Автоматизирует решение типовых задач: сохранение сущностей, выборки, фильтрация
Таким образом, для каждой записи в БД мы имеем .NET обертку, со свойствами и методами, а ля ActiveRecord. Свойства и методы могут скрывать различные аспекты бизнес-логики, будь то автоматический расчет дополнительных значений, верификация пользовательского ввода, запуск различных механизмов. Например, сущность "комментарий блога" представлена классом BXBlogComment, у которого, как подсказывает IntelliSense smile:)



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



но может быть и задано вручную. Удобно, не правда ли?

Заглядывая внутрь BXBlogComment можно найти метод GetList. Этот могущественный метод - основа всей ORM, который опирается на механизм фильтрации и выборки, но об этом чуть позже. Рассмотрим типовой пример из модуля "Блоги":

  1. BXBlogCommentCollection commentCollection = BXBlogComment.GetList(
  2. new BXFilter(
  3. new BXFilterItem(BXBlogComment.Fields.Post.Id, BXSqlFilterOperators.Equal, 256),
  4. new BXFilterItem(BXBlogComment.Fields.LiveRootNodeIndex, BXSqlFilterOperators.Greater, 3),
  5. new BXFilterItem(BXBlogComment.Fields.LiveRootNodeIndex, BXSqlFilterOperators.LessOrEqual, 7)
  6. ),
  7. new BXOrderBy(new BXOrderByPair(BXBlogComment.Fields.LeftMargin, BXOrderByDirection.Asc)),
  8. new BXSelectAdd(
  9. BXBlogComment.Fields.AuthorBlog.Id,
  10. BXBlogComment.Fields.AuthorBlog.Slug,
  11. BXBlogComment.Fields.AuthorBlog.Categories.Category.Sites.SiteId,
  12. BXBlogComment.Fields.Author,
  13. BXBlogComment.Fields.Author.User,
  14. BXBlogComment.Fields.Author.User.Image
  15. ),
  16. null
  17. );


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

Первым аргументом 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-кода защищает нас от различных дыр в безопасности.

Как с этим работать

В ходе разработки сайта или модуля разработчикам, возможно, придется столкнуться со следующими задачами:
  • Описание сущностей
  • Использование сущностей
Описание сущности сводится к объявлению 3-х классов: схемы сущности, собственно сущности и класса коллекции

Класс схемы сущности наследуется от BXScheme и содержит в своем статическом конструкторе (на данный момент это особенность API) описания полей, а также предоставляет доступ к полям через свойства. Например, схема Вашей сущности может выглядеть следующим образом:

  1. public class CustomerScheme : BXScheme<CustomerScheme>
  2. {
  3. static CustomerScheme()
  4. {
  5. SetTable("customer", "cm");
  6.  
  7. AddField("Id", new BXTableField("id", SqlDbType.Int, 0, false, true, true));
  8. AddField("Name", new BXTableField("name", SqlDbType.NVarChar, 256, false, ""));
  9. AddField("Birthday", new BXTableField("birthday", SqlDbType.DateTime, true, x => DateTime.Now));
  10. }
  11.  
  12. public BXSchemeFieldBase Id { get { return GetField("Id"); } }
  13. public BXSchemeFieldBase Name { get { return GetField("Name"); } }
  14. public BXSchemeFieldBase Birthday { get { return GetField("Birthday"); } }
  15. }


Класс сущности наследуется от BXEntity и содержит в себе описание свойств сущности и пользовательскую бизнес-логику. В самом простом случае это будет выглядеть так:

  1. public class Customer : BXEntity<Customer, CustomerCollection, CustomerScheme>
  2. {
  3. public override string EntityId { get { return "Customer"; } }
  4. public override string ModuleId { get { return "MyModule"; } }
  5.  
  6. public int Id
  7. {
  8. get { return GetValue<int>("Id"); }
  9. }
  10. public string Name
  11. {
  12. get { return GetValue<string>("Name") ?? ""; }
  13. set { SetValue("Name", value ?? ""); }
  14. }
  15. public DateTime Birthday
  16. {
  17. get { return GetValue<DateTime>("Birthday"); }
  18. set { SetValue("Birthday", value != DateTime.MinValue ? (object)value : null); }
  19. }
  20. }


Класс коллекции - на данном этапе это просто обертка и описывается одной строкой:

  1. public class CustomerCollection : BXEntityCollection<Customer, CustomerCollection, CustomerScheme> {}


Таким образом для типовых сущностей задача описания носит действительно описательный характер. А с написанием кода может справиться, например, какой-нибудь кодогенератор. smile:)

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

Для критичных операций можно написать методы с прямыми SQL запросами - API сущностей позволяет создать сущность из голой SQL выборки SqlDataReader'ом.

В конечном итоге мы получаем унифицированный механизм для доступа к данным - все сущности имеют метод GetList, набор полей и типизированные свойства. Т.е. нам не придется задумываться, а как выбрать тот или иной объект по такому-то условию - методы выборки одинаковы для всех сущностей, а Visual Studio подскажет, какие поля или свойства есть у сущности.

Заключение

API сущностей - это уже сложившийся механизм, который прошел \"боевое крещение\" инфоблоками, форумами и блогами, и призван избавить разработчика от необходимости писать рутинные SQL запросы для типовых задач, задействовать особенности статической типизации и использовать удобные средства, которые нам предлагает IDE.
Dmitry Pyalov [Дмитрий Пялов]
26.05.200913:0926.05.2009 13:09:15
Просмотров:2349 Комментариев:5 0
Теги: .net, orm, новая версия
*
 
Незарегистрированным пользователям запрещена вставка ссылок. Зарегистрируйтесь или авторизуйтесь.
*
Добавить комментарий
0
Крестьянинов Михаил
27.05.2009 06:12:25
Браво! Обязательно дайте почитать этот пост Вашим коллегам из PHP-проекта smile:)
Ответить Ссылка 0
0
Сергеева Наталья
01.06.2009 12:28:16
Спасибо, Дим! Даже я поняла, что для меня, как сущности из класса "Пользователь" теперь не нужно все писать с нуля - если декларативно определить схему таблиц базы данных, то все остальное сделает сама умная ORM-система smile:)

Ответить Родитель Ссылка 0
0
Ковалев Сергей
08.06.2009 13:56:06
Неужто нельзя было использовать LINQ вместо GetList()? Неудобно.
Ответить Ссылка 0
0
Dmitry Pyalov [Дмитрий Пялов]
09.06.2009 11:52:32
LINQ - это особенность C# 3.0 - работа над ORM велась еще в те времена, когда не было ни .NET 3.5 ни ADO.NET Entities. И в принципе этого механизма достаточно, чтобы решать повседневные задачи, хотя не спорю, с LINQ это бы выглядело более естественно.

Поддержку LINQ можно реализовать, например, как стороннее решение.
Ответить Родитель Ссылка 0
0
MS Fan
19.06.2009 14:58:34
Дмитрий, технология LINQ была включена в Visual Studio 2008, которую выпустили в конце 2008. Уверен, что как раз на ней вы и стали разрабатывать Битрикс .NET smile;)
Если честно, то метод BXBlogComment.GetList выглядит ужасно smile:o Писать и тем более читать такой код сложновато. А что можно было бы сделать с помощью Entity Framework и LINQ.
Код
var comments = from c in Entities.Comments 
               where c.Id == 256 && c.Foo == "bar" // и т.д.

Про ASP.NET MVC спрашивать не буду. Разработчикам нравится работать с передовыми технологиями, которые обеспечивают более высокую продуктивность. Надеюсь, что скоро БУС.NET догонит и перегонит мир ASP.NET
Ответить Родитель Ссылка 0

Добавить комментарий
 
Технологии Эрмитаж
BitrixMobile
Автокеширование
SiteUpdate
Производительность Виртуальная машина
Веб-окружение
Результаты тестов
Выбрать хостинг
Веб-кластер
Безопасность Проактивная защита
Веб-антивирус
Аутентификация

Контакты Поиск Карта сайта
Телефон: +7 (495) 229-14-41
Оставайтесь с нами: Facebook Twitter Habrahabr VKontakte Developers Google 1+
Как распознать QR код?Контакты QR


© 2001-2012 «Битрикс», «1С-Битрикс». Работает на 1С-Битрикс: Управление сайтом.
Английская версия Немецкая версия