Использование возможностей NHibernate в Orchard.CMS

    Orchard.CMS одна из популярных свободных open source систем управления веб контентом на базе .NET. В качестве ORM для доступа к данным используется NHibernate. Более детальную информацию можно найти на официальном сайте проекта, к тому же на Хабре уже были статьи посвященные Orchard.CMS.
    Orchard CMS используется свой способ создания схемы данных посредством Migration и SchemeBuilder. Для доступа к сессии NHiberanate (ISession) и транзакциям используется специализированные интерфейсы, инкапсулирующие эти объекты внутри (ISessionHolder и ITransactionManager). Организованы собственные интерфейсы репозиториев (IRepository), реализации которых работают поверх NHibernate Linq Query.
    Orchard не предусматривает прямого доступа к NHibernate по умолчанию. Ниже будут рассмотрены особенности построения и использования доменной модели на базе Orchard CMS, а также способ использования NHibernate напрямую из своего модуля.
    Если бизнес уровень инкапсулирован отдельно, и Orchard.CMS обращается к сущностям по средствам веб-сервисов, проблема построения доменной модели не возникает. Это относиться к крупным проектам. Исследования в данной статье будут справедливы для проектов, в которых изначально планируется использовать общую базу и для Orchard CMS, и для сущностей бизнес логики.

    Доменная модель на базе Orchard.CMS (ContentTypeDefinition)

    Рассмотрим модель BlogPost в базовом модуле Блога в Orchard.CMS. (Исходный код проекта можно найти на официальном сайте). Модель типа BlogPost блога:
    • BlogPost – тип контента (BlogPost — ContentTypeDefinition). Он состоит из следующих частей:
      • BlogPostPart – контентная часть отвечающая за описание блога.
      • CommonPart – стандартная контентная часть, инкапсулирует информацию об авторе и версии.
      • PublishLaterPart – контентная часть для реализации черновиков.
      • TitlePart – титульная часть.
      • AutoroutPart – красивые URL.
      • BodyPart – собственно тело записи блога.

    В модуле блога реализован widget позволяющий вывести последние N записей блога. Посмотрим на SQL запрос, который формируются для получения этого списка. Для этого воспользуемся NHProfiler и подключим модуль SoNerdy.NHProf в Orchard.CMS. (Одна из рекомендаций при разработке на Orchard.CMS – это использование NHibernate Profiler www.hibernatingrhinos.com/products/nhprof. Данная утилита незаменима в анализе и оптимизации сайта.)
    Запрос, выбирающий N записей блогов, выглядит следующим образом. Join полей дополнительных контентных частей были специально удалены, чтобы сосредоточиться на базовых частях.

    SELECT top 12 ...
    FROM   v1__Orchard_Framework_ContentItemVersionRecord this_
           inner join v1__Orchard_Framework_ContentItemRecord contentite1_
             on this_.ContentItemRecord_id = contentite1_.Id
           inner join v1__Common_CommonPartRecord commonpart3_
             on contentite1_.Id = commonpart3_.Id
           inner join v1__Orchard_Framework_ContentTypeRecord contenttyp2_
             on contentite1_.ContentType_id = contenttyp2_.Id
    WHERE  contenttyp2_.Name in ('BlogPost' /* @p0 */)
           and commonpart3_.Container_id = 22 /* @p1 */
           and this_.Published = 1 /* @p2 */
    ORDER  BY commonpart3_.CreatedUtc desc
    


    Краткий анализ запроса:
    • Для получения только базовой информации контентного типа необходимо как минимум три Inner Join.
    • Базовая структура всех определенных в Orchard.CMS контентных типах содержится в таблицах: ContentItemVersionRecord, ContentItemRecord и ContentTypeRecord

    Результатом реализации доменной модели приложения в рамках контентных типов Orchard.CMS будет следующее:
    • Все сущности будут иметь данные в одних и тех же таблицах. К примеру, для e-commerce, Id продуктов, заказов, клиентов будут храниться в одних и тех же таблицах.
    • Даже небольшие запросы будут выбирать всю информацию о контентной части. К примеру, если необходимо получить название производителя для отображения под продуктом в списке. В рамках реализации Orchard будут выбраны все данные из контентной части. В противном случае теряется смысл использования техники динамического представления (Shape).
    • Задача построения отчетов напрямую из базы данных очень сильно усложняется. Очень много Join.
    • Миграция данных средствами базы в рамках данной реализации очень. Очень много Join.
    • Снижение производительности за счет избыточного количества запросов. Очень много внимания нужно уделить на работу с Profiler, для определения узких мест.
    • Orchard.CMS базируется на контентных частях и контентных типах. Текстовые разделы, блоги, html части и так далее – в большинстве случаев контентные типы или контентные части. Смешивание данные представления и доменной модели приложения – грубое нарушение инкапсуляции.
    • Перенести доменную модель и бизнес логику, выполненные в контексте Orchard, на другую CMS или чистый MVC – это огромная работа.
    • Тестирование, его придется выполнять в рамках Orchard контекста.


    Как вариант решения проблемы – полностью отказ от использования контентных типов при построении доменной модели. Ее реализация при помощи простых Record классов. Orchard использует AutoMapping для конфигурации NHibernate, и одна из конвенций следующая: ко всем названиям типов данных необходимо добавлять постфикс Record. Минус в том, что тестирование по-прежнему будет зависеть от контекста Orchard, и миграция в другую систему управления контентом усложниться. К тому же необходимо реализовывать отдельные модули для бизнес логики и для представления.

    Использование NHibernate напрямую в Orchard.CMS

    Framework Orchard.CMS предоставляет возможности конфигурировать HNibernate и использовать его возможности напрямую, без Orchard pipeline. Начиная с версии 1.7 стал доступен новый интерфейс ISessionConfigurationEvents. Пример реализации ISessionConfigurationEvents в демонстрационном проекте:

    public class PersistenceConfiguration : ISessionConfigurationEvents
        {
            public PersistenceConfiguration()
            {
                Logger = NullLogger.Instance;
            }
    
            public ILogger Logger { get; set; }
    
            public void Created(FluentConfiguration cfg, AutoPersistenceModel defaultModel)
            {
                cfg.Mappings(x => x.FluentMappings.AddFromAssemblyOf<Customer>());
            }
    ...
    
            public void ComputingHash(Hash hash)
            {
                hash.AddString("NHStore.Domain.Mapping");
            }
    }
    

    Для конфигурации своего модуля необходимо добавить реализацию этого интерфейса в свой модуль и определить конфигурацию NHibernate в методе Created. Также необходимо определить Hash модуля для автоматической перегенерации общей конфигурации NHibernate. Orchard.CMS генерирует конфигурацию NHibernate в файл mapping.bin, который находиться в папке App_Data\Sites\Default, отдельная конфигурация для каждого сайта. Для перегенерации существующей конфигурации, необходимо удалить mapping файл, и приложение создаст его автоматически.

    Для доступа к сущностям возможно использовать существующие интерфейсы в Orchard.CMS:

    IRepository — стандартный интерфейс, реализация, которого использует Linq To NHibernate Cacheble Query. Основные методы:
    • void Create(T entity); — Save
    • void Update(T entity); — Evict/Merge
    • void Delete(T entity); — Delete
    • IEnumerable Fetch(); — ToReadOnlyCollection
    • IQueryable Table { get; } – LinqToNHibernate Query Object

    ISessionLocator – интерфейс в Orchard.CMS, который предоставляет доступ к объекту интерфейса ISession. Основные методы:
    • ISession For(Type entityType); — передается тип сущности, который используется только для логирования (Logger.Debug(«Acquiring session for {0}», entityType); Объект сессии создается один на каждый запрос.

    Необходимо упомянуть о транзакциях в Orchard. По умолчанию Orchard.CMS создает одну транзакцию на весь запрос и выполняет ее Commit после завершения запроса. Если запрос выполняется успешно – commit выполняется, если нет – происходит откат транзакции. Уровень изоляции данных по умолчанию – ReadCommitted. Для того чтобы завершить текущую транзакцию и открыть новую необходимо воспользоваться интерфейсом ITransactionManager. Этот интерфейс предоставляет методы для работы с транзакциями.

    Удалось протестировать и реализовать демонстрационный проект с использованием Fluent NHibernate Mapping конфигурации доменной модели определенной в отдельной сборке. Проект находиться на github и доступен для скачивания.

    Данная статья является рекомендацией к реализации проектов с использованием Orchard.CMS. Буду рад, если в комментариях опишут другие эффективные подходы для реализации доменной модели в рамках данной системы управления контентом.
    Поделиться публикацией

    Похожие публикации

    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

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

      0
      Это всё хорошо, а нагрузочное тестирование различных вариантов проводились?
        0
        Тестирования пока не было, т.к. сделан только рабочий демо проект, в перспективе будет. Но собственно ради чего все и началось, сайт с моделью построенной на базе ContentTypeDefinition жутко тормозил. Когда прошелся профайлером: для построения одной страницы из списка 12 картин, требовалось порядка 600 запросов к базе. После оптимизации удалось снизить до 160, но это все равно много, поэтому было принято решение сделать исследование в пользу отдельного построения модели, независимой от Orchard.
          0
          Тогда не забудьте с нами поделиться, когда проведёте. Было бы очень интересно. ))
        0
        «Orchard.CMS одна из популярных свободных open source систем управления веб контентом на базе .NET.»

        Всего 20K загрузок с июля месяца.
          0
          В компании проводили анализ и сравнение CMS-к на ASP.NET, как проприетарных, так и свободных. Umbraco и Orchard.CMS лучшие свободные решения в области веб-контент менеджмента построенные на .NET.
            0
            Чтобы не быть голословным: Orchard и Umbraco в первой пятерке (язык=eng): Галерея веб-CMS на Microsoft
            0
            Статья в целом полезная, хотя и немного странная. Не хватает информации, которая логически бы связывала ее части друг с другом. Например, в самом начале вы пишите:
            > Orchard не предусматривает прямого доступа к NHibernate по умолчанию
            хотя зачем это может понадобится из информации во введении непонятно. К тому же позже как раз показано, как конфигурировать NH напрямую. Так что тут либо следовало написать, что подразумевается под «по умолчанию», либо использовать другую формулировку.

            Первая часть начинается вроде бы стройно: показаны проблемы при использовании типов содержимого (это более распространненный перевод для content type, чем контентный тип). После этого в результатах применения подхода с типами содержимого написано:
            > Orchard.CMS базируется на контентных частях и контентных типах. Текстовые разделы, блоги, html части и так далее – в большинстве случаев контентные типы или контентные части. Смешивание данные представления и доменной модели приложения – грубое нарушение инкапсуляции.

            Это звучит как 3 несвязанных друг с другом предложений. Orchard.CMS — это прежде всего CMS, как бы смешно это ни звучало, и указанные сущности (текстовые разделы, блоги, html части) являются скорее ее моделью, а не представлением. С последним предложением в целом можно согласится (хотя часто в представлениях можно использовать модель напрямую, напр. когда это не требует изменений модели в простых views и не влечет проблем с производительностью), но как это связано с предыдущими двумя предложениями, непонятно.

            > Перенести доменную модель и бизнес логику, выполненные в контексте Orchard, на другую CMS или чистый MVC – это огромная работа.

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

            > Тестирование, его придется выполнять в рамках Orchard контекста.

            Имеется ввиду юнит-тестирование? Если да, то какие проблемы это влечет? Orchard построен на интерфейсах и очень гибок в этом смысле. Да придется потратить больше времени на создание mock-ов для Orchard-овских сущностей, но его также придется потратить и при использовании других подходов. Т.е. опять недостаточно информации.

            После этого вскользь упоминается паттерн Record и начинается вторая часть об использовании NH напрямую. Как вторая часть связана со второй? Какой подход вы в итоге использовали? Проанализировав код конфигурации NH можно сделать вывод, что вы скорее всего используете собственную модель, вместо типов содержимого:
            > cfg.Mappings(x => x.FluentMappings.AddFromAssemblyOf());
            но это все неявно. Нужно было более детально описать используемый для статьи тестовый сценарий, тогда бы она выглядела как цельная статья. Сейчас она больше похожа на несколько статей из справочной документации.

            Не относитесь к этой критике негативно. Чем больше будет статей про Orchard, тем лучше, на мой взгляд. Это хорошая, хотя и своеобразная CMS, написанная и поддерживаемая грамотными людьми. Поэтому в целом я считаю инвестиции в нее оправданными и все последние свои проекты делаю на Orchard-е. Те, кто работал с Orchard-ом скорее всего поймут посыл статьи, но хотелось бы также, чтобы и новички не испугались ее использовать после прочтения. Для этого я и добавил сюда этот комментарий.
              0
              Спасибо большое за развернутый комментарий. Соглашусь, что статья получилась сложная и узкоспециализированная.
              Сделать полноценный туториал для новичков не удалось, т.к. материал возрос бы раза в 3 в 4, а это довольно затратно по времени. Постарался донести кратко основные идеи, которые вы абсолютно верно отметили, и представил готовый демо проект.

              Пока что я не реализовал данную модель в бою, но в планах есть такое. Много всего еще не затронуто здесь — это и кеширование модели и страниц, это и интеграция виджетов и доменной модели в плане архитектуры (грамотная разбивка на модули, расширяемость, mapping).

              Orchard.CMS на мой взгляд имеет одну из самых сложных кривых обучения в сравнение с Ektron, Sitefinity, Umbraco или NopCommerce. Если делать выбор в пользу открытой CMS, я бы остановил свой выбор на Umbraco, а для E-Commerce — NopCommerce.
                0
                я выбирал между Umbraco и Orchard, остановился на последнем, как на более перспективном. Umbraco в свое время представляла из себя смесь из xslt и web forms, и ни то ни другое мне не нравилось (и сейчас не нравится). Как там сейчас обстоит дело не знаю, вроде они в сторону mvc тоже двигаются.
                  0
                  В Umbraco можно комбинировать все подходы и XSLT, и WebForms, и MVC.
                  Umbraco является хорошим выбором CMS для проекта где участвуют более трех разработчиков, тем более с разным уровнем подготовки.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое