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

в 18:22, , рубрики: .net, 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_DataSitesDefault, отдельная конфигурация для каждого сайта. Для перегенерации существующей конфигурации, необходимо удалить 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. Буду рад, если в комментариях опишут другие эффективные подходы для реализации доменной модели в рамках данной системы управления контентом.

Автор: Wertugo

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js