Объектная модель хранилищ Creatio
Glossary Item Box
Классы и интерфейсы Creatio для работы с хранилищем данных и кэшем
Для работы с хранилищем данных и кэшем в Creatio реализован ряд классов и интерфейсов, которые расположены в пространстве имен Terrasoft.Core.Store ("Библиотеки классов серверной части ядра платформы"). Ниже кратко перечислены основные из них.
Базовое хранилище IBaseStore
Базовые возможности всех типов хранилищ определяет интерфейс IBaseStore. Свойства и методы этого интерфейса реализуют:
- Доступ к данным по ключу для чтения/записи (индексатор this[string key]).
- Удаление данных из хранилища по заданному ключу (метод Remove(string key)).
- Инициализация хранилища заданным списком параметров (метод Initialize(IDictionary parameters)). На текущий момент параметры для инициализации хранилищ Creatio вычитываются из конфигурационного файла. Списки параметров задаются в секциях storeDataAdapter (для хранилища данных), storeCacheAdapter (для кэша). Но в общем случае параметры могут задаваться произвольным образом.
Хранилище данных IDataStore
Интерфейс определяет специфику работы с хранилищами данных. Является наследником базового интерфейса хранилищ IBaseStore. Дополнительно в интерфейсе определена возможность получения списка всех ключей хранилища (свойство Keys).
Свойство Keys при работе с хранилищами данных желательно использовать только в исключительных случаях, когда решение задачи альтернативными способами невозможно.
Хранилище кэша ICacheStore
Интерфейс определяет специфику работы с кэшами. Является наследником базового интерфейса хранилищ IBaseStore. В интерфейсе определено свойство GetValues(IEnumerable keys), которое возвращает словарь объектов кэша с заданными ключами. Данный метод позволяет оптимизировать работу с хранилищем при одновременном получении набора данных.
Класс Store
Статический класс для доступа к кэшам и хранилищам данных различных уровней. Уровни хранилища данных и кэша определены в перечислениях Terrasoft.Core.Store.DataLevel и Terrasoft.Core.Store.CacheLevel соответственно (см. табл. 1 и 2).
Класс Store имеет два статических свойства:
- Свойство Data возвращает экземпляр провайдера хранилища данных.
- Свойство Cache возвращает экземпляр провайдера кэша.
В примере 1 продемонстрирована работа с кэшем и хранилищем данных с использованием класса Store.
Для обеспечения миграции от фреймворка ASP.NET Framework к ASP.Net Core начиная с версии приложения 7.14.2 изменился способ работы с хранилищами данных уровня Session и Request. Для корректной работы хранилищ в ASP.Net Core необходимо заменить использование статических свойств на работу через UserConnection (см. ниже)
Пример 1
// Внимание! Код некорректный начиная с версии 7.14.2.! // Получение ссылки на хранилище данных приложения уровня сессии. IDataStore dataStore = Store.Data[DataLevel.Session]; // Помещение в хранилище данных значения "Data Test Value" с ключом "DataKey". dataStore["DataKey"] = "Data Test Value"; // Получение ссылки на кэш приложения уровня рабочего пространства. ICacheStore cacheStore = Store.Cache[CacheLevel.Workspace]; // Удаление из кэша элемента с ключом "CacheKey". cacheStore.Remove("CacheKey");
Доступ к хранилищам данных и кэшам из UserConnection
Доступ к хранилищам данных и кэшам приложения из конфигурационного кода можно получить, используя свойства статического класса Store пространства имен Terrasoft.Core.Store. Альтернативным вариантом доступа к хранилищу данных и кэшу в конфигурационной логике, который позволяет избежать использования длинных имен свойств и подключения дополнительных сборок, является доступ через экземпляр класса UserConnection.
В классе UserConnection реализован ряд вспомогательных свойств, позволяющих получить быстрый доступ к хранилищам данных и кэшу различных уровней:
- ApplicationCache возвращает ссылку на кэш уровня приложения.
- WorkspaceCache возвращает ссылку на кэш уровня рабочего пространства.
- SessionCache возвращает ссылку на кэш уровня сессии.
- RequestData возвращает ссылку на хранилище данных уровня запроса.
- SessionData возвращает ссылку на хранилище данных уровня сессии.
- ApplicationData возвращает ссылку на хранилище данных уровня приложения.
В большинстве случаев обращение к свойствам UserConnection идентично обращениям к свойствам Store.Cache и Store.Data с указанием соответствующих уровней. Однако в некоторых ситуациях (например, при запуске бизнес-процессов с помощью планировщика) может использоваться другая реализация. Поэтому в конфигурационной логике для доступа к хранилищам рекомендуется использовать свойства объекта UserConnection.
Для обеспечения миграции от фреймворка ASP.NET Framework к ASP.Net Core начиная с версии приложения 7.14.2 изменился способ работы с хранилищами данных уровня Session и Request. Для корректной работы хранилищ в ASP.Net Core необходимо заменить использование статических свойств на работу через UserConnection
Пример 2
// Ключ, с которым в кэш будет помещаться значение. string cacheKey = "SomeKey"; // Помещение значения в кэш уровня сессии через свойство UserConnection. UserConnection.SessionCache[cacheKey] = "SomeValue"; // Получение значения из кэша через свойство класса Store. // В результате переменная valueFromCache будет содержать значение "SomeValue". string valueFromCache = UserConnection.SessionCache[cacheKey] as String;
Использование кэша в EntitySchemaQuery
В EntitySchemaQuery реализован механизм работы с хранилищем (кэшем Creatio либо произвольным хранилищем, определенным пользователем). Работа с кэшем позволяет оптимизировать эффективность выполнения операций за счет обращения к закэшированным результатам запроса без дополнительного обращения к базе данных. При выполнении запроса EntitySchemaQuery данные, полученные из базы данных на сервере, помещаются в кэш, который определяется свойством Cache с ключом, который задается свойством CacheItemName. По умолчанию в качестве кэша запроса EntitySchemaQuery выступает кэш Creatio уровня сессии с локальным хранением данных. В общем случае в качестве кэша запроса может выступать произвольное хранилище, которое реализует интерфейс ICacheStore.
Ниже приведен пример работы с кэшем приложения при выполнении запроса EntitySchemaQuery (пример 3). В примере строится запрос, который возвращает список всех городов схемы City. При получении результатов выполнения запроса (после вызова метода GetEntityCollection()) эти результаты помещаются в кэш, откуда затем могут использоваться при повторном получении коллекции элементов запроса без дополнительного обращения к базе данных.
Пример 3
// Создание экземпляра запроса EntitySchemaQuery с корневой схемой City. var esqResult = new EntitySchemaQuery(UserConnection.EntitySchemaManager, "City"); // Добавление в запрос колонки с наименованием города. esqResult.AddColumn("Name"); // Определение ключа, под которым в кэше будут храниться результаты выполнения запроса. // В качестве кэша выступает кэш уровня сессии с локальным кэшированием данных (так как не // переопределяется свойство Cache объекта). esqResult.CacheItemName = "EsqResultItem"; // Выполнение запроса к базе данных для получения результирующей коллекции объектов. // После выполнения этой операции результаты запроса будут помещены в кэш. При дальнейшем обращении к // esqResult для получения коллекции объектов запроса (если сам запрос не был изменен) эти объекты будут // браться из сессионного кэша. esqResult.GetEntityCollection(UserConnection);
Прокси-классы хранилища и кэша
Понятие и назначение прокси-классов
Доступ к хранилищам и кэшам в Creatio может быть осуществлен как напрямую (через свойства класса Store), так и через прокси-классы.
Прокси-классы — это специальные объекты, которые представляют собой промежуточное звено между хранилищами и вызывающим кодом. Прокси-классы позволяют выполнять промежуточные действия над данными перед их чтением/записью в хранилище. Особенностью прокси-классов является то, что каждый из них является хранилищем.
Области применения прокси-классов
1. Первоначальная настройка и конфигурирование приложения
В конфигурационном файле Web.config можно настроить использование прокси-классов для хранилищ данных и кэшей. Настройка прокси-классов для соответствующего хранилища данных или кэша осуществляется в секциях storeDataAdapter и storeCacheAdapter соответственно. В них добавляется секция proxies, в которой перечисляются все прокси-классы, применяемые к хранилищу (пример 4).
Пример 4
<storeDataAdapters> <storeAdapter levelName="Request" type="RequestDataAdapterClassName"> <proxies> <proxy name="RequestDataProxyName1" type="RequestDataProxyClassName1" /> <proxy name="RequestDataProxyName2" type="RequestDataProxyClassName2" /> <proxy name="RequestDataProxyName3" type="RequestDataProxyClassName3" /> </proxies> </storeAdapter> </storeDataAdapters> <storeCacheAdapters> <storeAdapter levelName="Session" type="SessionCacheAdapterClassName"> <proxies> <proxy name="SessionCacheProxyName1" type="SessionCacheProxyClassName1" /> <proxy name="SessionCacheProxyName2" type="SessionCacheProxyClassName2" /> </proxies> </storeAdapter> </storeCacheAdapters>
При загрузке приложения настройки считываются из конфигурационного файла и применяются к соответствующему виду хранилища. Таким образом можно выстраивать цепочки прокси-классов, которые будут выполняться последовательно один за другим. Порядок прокси-классов в цепочке выполнения соответствует их порядку в конфигурационном файле. При этом первым в цепочке выполнения выступает прокси-класс, перечисленный последним в секции proxies, то есть выполнение осуществляется "снизу вверх".
При этом необходимо учитывать следующее:
- "Конечной точкой" применения цепочки прокси-классов является конкретный кэш или хранилище данных, для которого эта цепочка определяется.
- Отдельно взятый прокси-класс знает, с каких кэшем или хранилищем он работает — ссылка на него определяется свойствами ICacheStoreProxy.CacheStore или IDataStoreProxy.DataStore. Однако, на что именно ссылается это свойство (на другой прокси-класс, на конечное хранилище или кэш), для прокси-класса неизвестно. При этом сам прокси может выступать в качестве хранилища, с которым будут работать другие прокси-классы.
Так, в соответствии с настройками, приведенными в примере 4, цепочка выполнения прокси-классов, например, хранилища данных, будет следующей: RequestDataProxyName3 —> RequestDataProxyName2 —> RequestDataProxyName1 —> RequestDataAdapterClassName (конечное хранилище данных уровня запроса).
2. Разграничение данных различных пользователей приложения
С помощью прокси-классов решается задача изоляции данных между пользователями. Наиболее простым решением этой задачи является трансформация ключей значений перед помещением их в хранилище (например, путем добавления к ключу дополнительного префикса, специфичного для конкретного пользователя). Использование таких прокси-классов обеспечивает уникальность ключей хранилища. Это, в свою очередь, позволяет избежать потери и искажения данных при одновременной попытке разных пользователей записать в хранилище различные значения с одинаковым ключом. Пример работы с прокси-классами трансформации ключей приведен ниже. В общем случае в прокси-классах можно реализовать насколько угодно сложную логику.
3. Выполнение других промежуточных действий с данными перед помещением их в хранилище
В прокси-классах можно реализовывать логику выполнения любых произвольных действий с данными перед помещением их в хранилище или получения их из него. Вынесение логики обработки данных в прокси-класс позволяет избежать дублирования кода, что, в свою очередь, облегчает его модификацию и сопровождение.
Базовые интерфейсы прокси-классов
Чтобы класс можно было использовать как прокси для работы с хранилищами, он должен реализовывать один или оба интерфейса пространства имен Terrasoft.Core.Store:
- IDataStoreProxy — интерфейс прокси-классов хранилища данных.
- ICacheStoreProxy — интерфейс прокси-классов кэша.
Каждый из этих интерфейсов имеет одно свойство — ссылку на то хранилище (или кэш), с которым работает данный прокси-класс. Для интерфейса IDataStoreProxy это свойство DataStore, а для интерфейса ICacheStoreProxy — свойство CacheStore.
Прокси-классы трансформации ключей
В Creatio реализован ряд прокси-классов, реализующих логику трансформации ключей значений, помещаемых в хранилища.
1. Класс KeyTransformerProxy
Это абстрактный класс, который является базовым классом для всех прокси-классов, преобразующих ключи кэша. Он реализует методы и свойства интерфейса ICacheStoreProxy. Чтобы избежать дублирования логики, при создании пользовательских прокси-классов для трансформации ключей рекомендуется наследоваться от этого класса.
2. Класс PrefixKeyTransformerProxy
Прокси-класс, преобразующий ключи кэша путем добавления к ним заданного префикса.
В примере 5 демонстрируется работа с кэшем уровня сессии через прокси-класс PrefixKeyTransformerProxy.
Пример 5
// Ключ, с которым значение будет помещаться в кэш через прокси. string key = "Key"; // Префикс, который будет добавляться к ключу значения прокси-классом. string prefix = "customPrefix"; // Создание прокси-класса, который будет использоваться для записи значений в кэш уровня сессии. ICacheStore proxyCache = new PrefixKeyTransformerProxy(prefix, Store.Cache[CacheLevel.Session]); // Запись значения с ключом key в кэш через прокси-класс. // Фактически это значение записывается в глобальный кэш уровня сессии с ключом prefix + key. proxyCache[key] = "CachedValue"; // Получение значения по ключу key через прокси. var valueFromProxyCache = (string)proxyCache[key]; // Получение значения по ключу prefix + key непосредственно из кэша уровня сессии. var valueFromGlobalCache = (string)UserConnection.SessionCache[prefix + key];
В итоге переменные valueFromProxyCache и valueFromGlobalCache будут содержать одинаковое значение "CachedValue".
3. Класс DataStoreKeyTransformerProxy
Прокси-класс, преобразующий ключи хранилища данных путем добавления к ним заданного префикса.
В примере 6 демонстрируется работа с хранилищем данных через прокси-класс DataStoreKeyTransformerProxy.
Пример 6
// Ключ, с которым значение будет помещаться в хранилище через прокси. string key = "Key"; // Префикс, который будет добавляться к ключу значения прокси-классом. string prefix = "customPrefix"; // Создание прокси-класса, который будет использоваться для записи значений в хранилище уровня сессии. IDataStore proxyStorage = new DataStoreKeyTransformerProxy(prefix) { DataStore = Store.Data[DataLevel.Session] }; // Запись значения с ключом key в хранилище через прокси-класс. // Фактически это значение записывается в глобальное хранилище уровня сессии с ключом prefix + key. proxyStorage[key] = "StoredValue"; // Получение значения по ключу key через прокси. var valueFromProxyStorage = (string)proxyStorage[key]; // Получение значения по ключу prefix + key непосредственно из хранилища уровня сессии. var valueFromGlobalStorage = (string)UserConnection.SessionData[prefix + key];
В итоге переменные valueFromProxyStorage и valueFromGlobalStorage будут содержать одинаковое значение "StoredValue".
Локальное кэширование данных
На базе прокси-классов в Creatio реализован механизм локального кэширования данных. Основная цель кэширования данных — снижение нагрузки на сервер хранения данных и времени выполнения запросов при работе с редко изменяемыми данными.
Логику механизма локального кэширования реализует внутренний прокси-класс LocalCachingProxy. Этот прокси-класс выполняет кэширование данных на текущем узле web-фермы. Класс выполняет мониторинг времени жизни кэшированных объектов и получает данные из глобального кэша только в том случае, если кэшированные данные не актуальны.
Для применения механизма локального кэширования на практике используются методы расширения для интерфейса ICacheStore из статического класса CacheStoreUtilities:
- WithLocalCaching() — переопределенный метод, который возвращает экземпляр класса LocalCachingProxy, выполняющего локальное кэширование.
- WithLocalCachingOnly(string) — метод, выполняющий локальное кэширование данных заданной группы элементов с мониторингом их актуальности.
- ExpireGroup(string) — метод устанавливает признак устаревания для заданной группы элементов. При вызове этого метода все элементы заданной группы становятся неактуальными и не возвращаются при запросе данных.
В примере 7 демонстрируется работа с кэшем рабочего пространства с использованием локального кэширования.
Пример 7
// Создание первого прокси-класса, который выполняет локальное кэширование данных. Все элементы, // записываемые в кэш через этот прокси, принадлежат к группе контроля актуальности Group1. ICacheStore cacheStore1 = Store.Cache[CacheLevel.Workspace].WithLocalCaching("Group1"); // Добавление элемента в кэш через прокси-класс. cacheStore1["Key1"] = "Value1"; // Создание второго прокси-класса, который выполняет локальное кэширование данных. Все элементы, // записываемые в кэш через этот прокси, также принадлежат к группе контроля актуальности Group1. ICacheStore cacheStore2 = Store.Cache[CacheLevel.Workspace].WithLocalCaching("Group1"); cacheStore2["Key2"] = "Value2"; // Для всех элементов группы Group1 устанавливается признак устаревания. Устаревшими считаются элементы // с ключами Key1 и Key2, так как они принадлежат к одной группе контроля актуальности Group1, несмотря на // то, что добавлены в кэш через разные прокси. cacheStore2.ExpireGroup("Group1"); // Попытка получения значений из кэша по ключам Key1 и Key2 после того, как эти элементы были помечены как // устаревшие. В результате переменные cachedValue1 и cachedValue2 будут содержать значение null. var cachedValue1 = cacheStore1["Key1"]; var cachedValue2 = cacheStore1["Key2"];