Использование EntitySchemaQuery для построения запросов к базе данных
Glossary Item Box
Общие положения
Назначение EntitySchemaQuery. Отличие от Select
EntitySchemaQuery – это высокоуровневый класс, который предназначен для построения запросов на выборку из базы данных. Для выполнения запросов к базе данных в bpm'online также предназначен класс Select. Однако между Select и EntitySchemaQuery существуют принципиальные различия.
Класс Select представляет собой стандартную SQL-инструкцию SELECT, которая позволяет делать выборку одной или нескольких строк или столбцов из одной или нескольких таблиц базы данных. Запрос строится "как есть": в запрос добавляются колонки, указываются источники данных, накладываются фильтры и условия. Результаты выполнения запроса возвращаются в виде экземпляра IDataReader. Ниже приведен пример использования класса Select для получения набора контактов (пример 1).
Для успешной компиляции приведенных ниже примеров в проект должны быть добавлены директивы using: using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data; using Terrasoft.Common; using Terrasoft.Core; using Terrasoft.Core.DB; using Terrasoft.Core.Entities; |
Пример 1
// Создание экземпляра запроса, добавление в запрос колонок и источника данных. Select selectQuery = new Select(UserConnection) .Column("Id") .Column("Name") .From("Contact"); // Выполнение запроса к базе данных и получение результирующего набора данных. using (DBExecutor dbExecutor = UserConnection.EnsureDBConnection()) { using (IDataReader reader = selectQuery.ExecuteReader(dbExecutor)) { while (reader.Read()) { // Обработка результатов запроса. } } }
Класс EntitySchemaQuery обладает функциональностью, аналогичной Select, которая расширена дополнительными возможностями по управлению правами доступа и работе с хранилищем (кэшем) bpm'online. По сути, EntitySchemaQuery является функционально расширенной надстройкой над классом Select. В конечном итоге все дополнительные свойства и параметры EntitySchemaQuery проецируются на экземпляр Select, который возвращает результирующий набор данных.
Метод GetSelectQuery() возвращает экземпляр Select, ассоциированный с конкретным запросом EntitySchemaQuery (пример 2).
Пример 2
// Создание экземпляра запроса EntitySchemaQuery. EntitySchemaQuery esq = new EntitySchemaQuery(UserConnection.EntitySchemaManager, "SomeSchema"); esq.AddColumn("SomeColumn"); // Получение экземпляра Select, ассоциированного с созданным запросом EntitySchemaQuery. Select selectEsq = esq.GetSelectQuery(UserConnection);
Результатом выполнения запроса EntitySchemaQuery является коллекция сущностей bpm'online – коллекция экземпляров класса Entity (экземпляр EntityCollection). Каждый экземпляр Entity в коллекции представляет собой строку набора данных, возвращаемого запросом. Получить результаты выполнения запроса можно следующими способами (пример 3):
- Получить весь результирующий набор данных, вызвав метод GetEntityCollection().
- Получить конкретную строку набора данных по заданному первичному ключу, вызвав метод GetEntity().
Пример 3
// Создание запроса к схеме City, добавление в запрос колонки Name. var esqResult = new EntitySchemaQuery(UserConnection.EntitySchemaManager, "City"); esqResult.AddColumn("Name"); // Выполнение запроса к базе данных и получение всей результирующей коллекции объектов. var entities = esqResult.GetEntityCollection(UserConnection); // Выполнение запроса к базе данных и получение объекта с заданным идентификатором. var entity = esqResult.GetEntity(UserConnection, new Guid("100B6B13-E8BB-DF11-B00F-001D60E938C6"));
Основными особенностями EntitySchemaQuery являются:
1) Поддержка прав доступа
Запрос на выборку данных EntitySchemaQuery строится таким образом, чтобы учитывать права текущего пользователя. То есть, в результирующий набор попадут только те данные, к которым текущий пользователь имеет доступ согласно его правам. Это обеспечивается за счет наложения дополнительных фильтров (условий) при формировании результирующего запроса к базе. Дополнительно для EntitySchemaQuery можно регулировать условия наложения прав на связанные таблицы, присутствующие в запросе (которые присоединяются к запросу предложением JOIN). Эти условия определяются значением свойства JoinRightState экземпляра EntitySchemaQuery. Более подробно об условиях наложения прав на связанные таблицы запроса изложено ниже.
2) Механизм кеширования
В EntitySchemaQuery реализован механизм работы с хранилищем (кэшем bpm'online либо произвольным хранилищем, определенным пользователем). Работа с кэшем позволяет оптимизировать эффективность выполнения операций за счет обращения к закэшированным результатам запроса без дополнительного обращения к базе данных. При выполнении запроса EntitySchemaQuery данные, полученные из базы данных на сервере, помещаются в кэш, который определяется свойством Cache с ключом, который задается свойством CacheItemName. По умолчанию в качестве кэша запроса EntitySchemaQuery выступает кэш bpm'online уровня сессии (данные доступны только в сессии текущего пользователя) с локальным хранением данных. В общем случае в качестве кэша запроса может выступать произвольное хранилище, которое реализует интерфейс ICacheStore. Пример работы с кэшем запроса EntitySchemaQuery приведен ниже (пример 4).
Пример 4
// Создание запроса к схеме City, добавление в запрос колонки Name. var esqResult = new EntitySchemaQuery(UserConnection.EntitySchemaManager, "City"); esqResult.AddColumn("Name"); // Определение ключа, под которым в кэше будут храниться результаты выполнения запроса. // В качестве кэша выступает кэш BPMonline уровня сессии с локальным кэшированием данных (так как не // переопределяется свойство Cache объекта). esqResult.CacheItemName = "EsqResultItem"; // Коллекция, в которую будут помещены результаты выполнения запроса. var esqCityNames = new Collection<string>(); // Коллекция, в которую будут помещаться закэшированные результаты выполнения запроса. var cachedEsqCityNames = new Collection<string>(); // Выполнение запроса к базе данных и получение результирующей коллекции объектов. // После выполнения этой операции результаты запроса будут помещены в кэш. var entities = esqResult.GetEntityCollection(UserConnection); // Обработка результатов выполнения запроса и заполнение коллекции esqCityNames. foreach (var entity in entities) { esqCityNames.Add(entity.GetTypedColumnValue<string>("Name")); } // Получение ссылки на кэш запроса esqResult по ключу CacheItemName виде таблицы данных в памяти. var esqCacheStore = esqResult.Cache[esqResult.CacheItemName] as DataTable; // Заполнение коллекции cachedEsqCityNames значениями из кэша запроса. if (esqCacheStore != null) { foreach (DataRow row in esqCacheStore.Rows) { cachedEsqCityNames.Add(row[0].ToString()); } }
3) Дополнительные настройки запроса
Для запросов EntitySchemaQuery можно определить дополнительные настройки, которые задают параметры для постраничного вывода результатов выполнения запроса, а также параметры построения иерархического запроса. Для этих целей предназначен класс EntitySchemaQueryOptions. Свойства этого класса позволяют определить:
- Имя колонки, использующейся для построения иерархического запроса (свойство HierarchicalColumnName);
- Начальное значение иерархической колонки, от которого будет строиться иерархия (свойство HierarchicalColumnValue);
- Максимальный уровень вложенности иерархического запроса (свойство HierarchicalMaxDepth);
- Значения условий постраничного вывода (свойство PageableConditionValues);
- Направление постраничного вывода (свойство PageableDirection);
- Количество записей страницы результирующего набора данных, возвращаемого запросом (свойство PageableRowCount).
Один и тот же экземпляр EntitySchemaQueryOptions можно использовать для получения результатов выполнения различных запросов, передавая его в качестве параметра методу GetEntityCollection() соответствующего запроса (пример 5).
Пример 5
// Создание экземпляра запроса с корневой схемой City. var esqCities = new EntitySchemaQuery(UserConnection.EntitySchemaManager, "City"); esqCities.AddColumn("Name"); // Создание запроса с корневой схемой Country. var esqCountries = new EntitySchemaQuery(UserConnection.EntitySchemaManager, "Country"); esqCountries.AddColumn("Name"); // Создание экземпляра настроек для возврата запросом первых 5 строк. var esqOptions = new EntitySchemaQueryOptions() { PageableDirection = PageableSelectDirection.First, PageableRowCount = 5, PageableConditionValues = new Dictionary<string, object>() }; // Получение коллекции городов, которая будет содержать первые 5 городов результирующего набора данных. var cities = esqCities.GetEntityCollection(UserConnection, esqOptions); // Получение коллекции стран, которая будет содержать первые 5 стран результирующего набора данных. var countries = esqCountries.GetEntityCollection(UserConnection, esqOptions);
Понятие корневой схемы. Построение путей к колонкам относительно корневой схемы. Примеры.
Отправной точкой механизма построения запроса EntitySchemaQuery является корневая схема.
Корневая схема – это схема (таблица в базе данных), относительно которой строятся пути ко всем колонкам в запросе, в том числе к колонкам присоединяемых таблиц.
При построении путей к колонкам применяется принцип обратной связи. Имя произвольной колонки, добавляемой в запрос, можно построить в виде цепочки взаимосвязанных звеньев, каждое из которых представляет собой "контекст" конкретной схемы, которая связывается с предыдущей по внешнему ключу (рис. 1).
Рис. 1. Взаимосвязи схем по внешним ключам
В общем случае формат построения имени произвольной колонки из схемы N можно представить в следующем виде:
[Контекст схемы 1].[...].[Контекст схемы N].[Имя_колонки]
Для того, чтобы в запрос добавить колонку из произвольной таблицы по обратным связям, необходимо корректно построить путь к этой колонке. Ниже рассмотрены различные варианты добавления колонок в запрос и примеры формирования имени к колонке в каждом варианте. Для всех приведенных ниже примеров в качестве корневой схемы выступает схема City.
1) Колонка корневой схемы
В этом случае имя колонки строится как [Имя колонки в корневой схеме].
- Пример: колонка с именем города
- Имя колонки: Name
- Пример создания запроса EntitySchemaQuery, возвращающего значения этой колонки:
// Создание экземпляра запроса EntitySchemaQuery с корневой схемой "City". var esqQuery = new EntitySchemaQuery(UserConnection.EntitySchemaManager, "City"); // Добавление в запрос колонки с именем города. esqQuery.AddColumn("Name"); // Получение текста результирующего sql-запроса. string esqSqlText = esqQuery.GetSelectQuery(UserConnection).GetSqlText();
- Результирующий sql-запрос (MS SQL):
SELECT [City].[Name] [Name] FROM [dbo].[City] [City]
2) Колонка схемы, на которую ссылается колонка-справочник текущей схемы
Имя колонки строится по принципу [Имя колонки-справочника].[Имя колонки схемы, на которую ссылается справочник].
При этом в результирующем запросе к корневой схеме City оператором JOIN (по умолчанию – LEFT OUTER JOIN) будет присоединена схема Country. Условие присоединения (условие ON оператора JOIN) формируется по следующему принципу:
[Имя присоединяемой схемы].[Id] = [Имя корневой схемы].[Имя колонки, которая ссылается на присоединяемую схему + Id]
- Пример: колонка с наименованием страны, которой принадлежит город
- Имя колонки: Country.Name
- Пример создания запроса EntitySchemaQuery, возвращающего значения этой колонки:
// Создание экземпляра запроса EntitySchemaQuery с корневой схемой "City". var esqQuery = new EntitySchemaQuery(UserConnection.EntitySchemaManager, "City"); // Добавление в запрос колонки с наименованием страны, которой принадлежит город. esqQuery.AddColumn("Country.Name"); // Получение текста результирующего sql-запроса. string esqSqlText = esqQuery.GetSelectQuery(UserConnection).GetSqlText();
- Результирующий sql-запрос (MS SQL):
SELECT [Country].[Name] [Country.Name] FROM [dbo].[City] [City] LEFT OUTER JOIN [dbo].[Country] [Country] ON ([Country].[Id] = [City].[CountryId])
В общем случае, можно продолжать построение цепочки обратных связей далее.
- Пример: получить имя контакта, который добавил страну конкретного города
- Имя колонки: Country.CreatedBy.Name
- Пример создания запроса EntitySchemaQuery, возвращающего значения этой колонки:
// Создание экземпляра запроса EntitySchemaQuery с корневой схемой "City". var esqQuery = new EntitySchemaQuery(UserConnection.EntitySchemaManager, "City"); // Добавление в запрос колонки с именем контакта, который добавил страну конкретного города. esqQuery.AddColumn("Country.CreatedBy.Name"); // Получение текста результирующего sql-запроса. string esqSqlText = esqQuery.GetSelectQuery(UserConnection).GetSqlText();
- Результирующий sql-запрос (MS SQL):
SELECT [CreatedBy].[Name] [CreatedBy.Name] FROM [dbo].[City] [City] LEFT OUTER JOIN [dbo].[Country] [Country] ON ([Country].[Id] = [City].[CountryId]) LEFT OUTER JOIN [dbo].[Contact] [CreatedBy] ON ([CreatedBy].[Id] = [Country].[CreatedById])
3) Колонка схемы по произвольному внешнему ключу
Имя колонки строится по принципу [Имя_присоединяемой_схемы:Имя_колонки_для_связи_присоединяемой_схемы:Имя_колонки_для_связи_текущей_схемы].
Если в качестве колонки для связи у текущей схемы выступает колонка Id, то в этом случае ее можно опустить, то есть имя колонки будет выглядеть следующим образом:
[Имя_присоединяемой_схемы:Имя_колонки_для_связи_присоединяемой_схемы].
- Пример: колонка с именем контакта, который добавил город
- Имя колонки: [Contact:Id:CreatedBy].Name
- Пример создания запроса EntitySchemaQuery, возвращающего значения этой колонки:
// Создание экземпляра запроса EntitySchemaQuery с корневой схемой "City". var esqQuery = new EntitySchemaQuery(UserConnection.EntitySchemaManager, "City"); // Добавление в запрос колонки с именем контакта, который добавил город. esqQuery.AddColumn("[Contact:Id:CreatedBy].Name"); // Получение текста результирующего sql-запроса. string esqSqlText = esqQuery.GetSelectQuery(UserConnection).GetSqlText();
- Результирующий sql-запрос (MS SQL):
SELECT [Contact].[Name] [Contact.Name] FROM [dbo].[City] [City] LEFT OUTER JOIN [dbo].[Contact] [Contact] ON ([Contact].[Id] = [City].[CreatedById])
В общем случае можно строить имена колонок по цепочкам обратных связей произвольной длины. В примере ниже рассматривается альтернативный вариант построения имени для колонки с именем контакта, добавившего страну конкретного города (см. п. 2).
- Пример: колонка с именем контакта, добавившего страну конкретного города
- Имя колонки: Country.[Contact:Id:CreatedBy].Name
- Пример создания запроса EntitySchemaQuery, возвращающего значения этой колонки:
// Создание экземпляра запроса EntitySchemaQuery с корневой схемой "City". var esqQuery = new EntitySchemaQuery(UserConnection.EntitySchemaManager, "City"); // Добавление в запрос колонки с именем контакта, добавившего страну конкретного города. esqQuery.AddColumn("Country.[Contact:Id:CreatedBy].Name"); // Получение текста результирующего sql-запроса. string esqSqlText = esqQuery.GetSelectQuery(UserConnection).GetSqlText();
- Результирующий sql-запрос (MS SQL):
SELECT [Contact].[Name] [Contact.Name] FROM [dbo].[City] [City] LEFT OUTER JOIN [dbo].[Country] [Country] ON ([Country].[Id] = [City].[CountryId]) LEFT OUTER JOIN [dbo].[Contact] [Contact] ON ([Contact].[Id] = [Country].[CreatedById])
Добавление колонок в запрос
Колонка запроса EntitySchemaQuery представляет собой экземпляр класса EntitySchemaQueryColumn. В свойствах экземпляра колонки можно указать ее основные характеристики: заголовок, значение для отображения, различные признаки ее использования, порядок и позицию сортировки и т.д. Полный перечень свойств и методов класса EntitySchemaQueryColumn приведен в соответствующем разделе описания классов SDK.
Для добавления колонок в запрос предназначен метод AddColumn(), который возврщает экземпляр добавленной в запрос колонки. Имя колонки относительно корневой схемы в методах AddColumn() формируется согласно правилам, описанным выше. Данный метод имеет ряд перегрузок, что позволяет добавлять в запрос колонки с различными параметрами (табл. 1).
Табл. 1. Список перегрузок метода AddColumn()
Перегрузка | Описание |
---|---|
EntitySchemaQuery.AddColumn(String,AggregationTypeStrict,EntitySchemaQuery) | Создает и добавляет колонку в запрос к схеме объекта в виде подзапроса, возвращающего результат указанной агрегирующей функции, по пути к колонке схемы относительно корневой схемы. |
EntitySchemaQuery.AddColumn(EntitySchemaQueryColumn) | Добавляет переданную колонку в коллекцию колонок запроса к схеме объекта. |
EntitySchemaQuery.AddColumn(String) | Создает и добавляет колонку в запрос к схеме объекта по заданному пути к колонке относительно корневой схемы. |
EntitySchemaQuery.AddColumn(EntitySchemaQueryFunction) | Создает и добавляет колонку в запрос к схеме объекта по переданной функции. |
EntitySchemaQuery.AddColumn(Object,DataValueType) | Создает и добавляет в запрос к схеме объекта колонку типа "параметр" с заданным значением определенного типа. |
EntitySchemaQuery.AddColumn(EntitySchemaQuery) | Создает и добавляет в качестве колонки запроса к схеме объекта переданный экземпляр подзапроса EntitySchemaQuery. |
Особенности использования присоединенных таблиц
Типы JOIN присоединенных таблиц
При добавлении в запрос колонки из схемы, отличной от корневой, эта схема добавляется в запрос посредством оператора JOIN. При этом по умолчанию применяется тип присоединения LEFT OUTER. EntitySchemaQuery позволяет при добавлении колонки некорневой схемы указать тип присоединения этой схемы к запросу. Для этого достаточно указать имя колонки в следующем формате:
[Спецсимвол типа соединения][Имя колонки]
EntitySchemaQuery поддерживает следующие типы соединений, которым соответствуют символы, приведенные в таблице 2.
Табл. 2. Типы JOIN присоединенных схем
Тип соединения | Спецсимвол типа соединения | Пример имени колонки |
---|---|---|
INNER JOIN | "=" | "=Name" |
LEFT OUTER JOIN | ">" | ">Name" |
RIGHT OUTER JOIN | "<" | "<Name" |
FULL OUTER JOIN | "<>" | "<>Name" |
CROSS JOIN | "*" | "*Name" |
Ниже приведен пример добавления в запрос колонок, используя различные типы соединения схем.
Пример 6
// Создание экземпляра запроса с корневой схемой City. var esqResult = new EntitySchemaQuery(UserConnection.EntitySchemaManager, "City"); // К запросу будет добавлена схема Country с типом присоединения LEFT OUTER JOIN. esqResult.AddColumn("Country.Name"); // К запросу будет добавленв схема Country с типом присоединения INNER JOIN. esqResult.AddColumn("=Country.Name"); // К запросу будут присоединены две схемы: // 1) схема Country с типом присоединения LEFT OUTER JOIN; // 2) схема Contact с типом присоединения RIGHT OUTER JOIN. esqResult.AddColumn(">Country.<CreatedBy.Name"); // Текст результирующего sql-запроса (MS SQL): // SELECT // [Country].[Name] [Country.Name], // [Country1].[Name] [Country1.Name], // [CreatedBy].[Name] [CreatedBy.Name] // FROM // [dbo].[City] [City] // LEFT OUTER JOIN [dbo].[Country] [Country] ON ([Country].[Id] = [City].[CountryId]) // INNER JOIN [dbo].[Country] [Country1] ON ([Country1].[Id] = [City].[CountryId]) // LEFT OUTER JOIN [dbo].[Country] [Country2] ON ([Country2].[Id] = [City].[CountryId]) // RIGHT OUTER JOIN [dbo].[Contact] [CreatedBy] ON ([CreatedBy].[Id] = [Country2].[CreatedById])
Наложение прав доступа на присоединенные схемы
Если корневая схема запроса администрируется по записям и в запросе присутствуют присоединяемые схемы, то к ним могут быть применены права доступа текущего пользователя. В таблице 3 приведены все возможные варианты наложения прав доступа к присоединенным схемам. Эти значения соответствуют членам перечисления Terrasoft.Core.DB.QueryJoinRightLevel.
Табл. 3. Варианты наложения прав доступа к присоединяемым схемам запроса
Значение члена перечисления | Порядок наложения прав доступа |
---|---|
EnabledAlways = 0 | Всегда накладывать права доступа. |
EnabledForAdditionalColumns = 1 | Накладывать права в том случае, если из связанной схемы в запросе используются колонки, отличные от первичной (PrimaryColumn) и первичной для отображения. |
Disabled = 2 | Не накладывать права доступа. |
Порядок применения прав доступа определяется значением свойства JoinRightState запроса. Значение этого свойства по умолчанию определяется системной настройкой QueryJoinRightLevel, которая принимает значения согласно табл. 3. Если значение этой системной настройки не задано, то ее значение по умолчанию принимается равным EnabledForAdditionalColumns.
Работа с фильтрами EntitySchemaQuery
Понятие фильтра EntitySchemaQuery и его структура
Фильтр - это набор условий, применяемых при отображении данных запроса. В терминах SQL фильтр представляет собой отдельный предикат (условие) оператора WHERE.
Любой фильтр как условие запроса имеет свою структуру (рис. 2).
Рис. 2. Структура фильтра EntitySchemaQuery
Filter = {[AggregationType] {<LeftExpression> | <LeftExpressionColumnPath>} <ComparisionType> {{<RightExpression> | {<RightExpressionColumnPath>,...}} | {<Macros>, [MacrosValue]}} }
Основные составляющие фильтра EntitySchemaQuery:
- AggregationType – тип агрегирующей функции, которая применяется к выражению в левой части условия. Необязательная составляющая фильтра. Задается значениями перечисления FilterAggregationType.
- LeftExpression – выражение в левой части фильтра. Задается экземпляром типа EntitySchemaQueryExpression.
- LeftExpressionColumnPath – путь к колонке, которая содержит выражение левой части фильтра. Задается строковым значением.
- ComparisionType – тип сравнения выражений в фильтре. Задается значением перечисления FilterComparisonType.
- RightExpression – выражение в правой части фильтра. Задается экземпляром типа EntitySchemaQueryExpression.
- RightExpressionColumnPath – путь к колонке, которая содержит выражение правой части фильтра. Задается строковым значением.
- Macros – макрос, который возвращает выражение для правой части фильтра. Задается значением перечисления EntitySchemaQueryMacrosType.
- MacrosValue - значение, которое передается в качестве параметра макросу Macros. Необязательный параметр. Задается экземплярами значений различных типов в зависимости от типа вызываемого макроса.
Создание и применение фильтров в EntitySchemaQuery
Для создания простого фильтра (рис. 2) в EntitySchemaQuery используется метод CreateFilter(), который возвращает экземпляр типа EntitySchemaQueryFilter. Для этого метода в EntitySchemaQuery реализован ряд перегрузок, что позволяет создавать фильтры с различными исходными параметрами.
Наряду с простыми фильтрами, в EntitySchemaQuery реализованы методы создания фильтров специального вида (табл. 4).
Табл. 4. Методы EntitySchemaQuery для создания фильтров специального вида
Метод создания фильтра | Описание |
---|---|
CreateFilterWithParameters() | Создает параметризированный фильтр для выборки записей по определенным условиям. Перегруженный метод. |
CreateIsNullFilter() | Создает фильтр сравнения типа [Является null в базе данных]. |
CreateIsNotNullFilter() | Создает фильтр сравнения типа [Не является null в базе данных]. |
СreateExistsFilter() | Создает фильтр сравнения типа [Существует по заданному условию]. |
CreateNotExistsFilter() | Создает фильтр сравнения типа [Не существует по заданному условию]. |
Экземпляр EntitySchemaQuery имеет свойство Filters, которое представляет собой коллекцию фильтров данного запроса (экземпляр класса EntitySchemaQueryFilterCollection, который, в свою очередь, представляет собой классическую типизированную коллекцию элементов IEntitySchemaQueryFilterItem). Для того, чтобы добавить фильтр в запрос, необходимо:
- создать экземпляр фильтра для данного запроса (методы CreateFilter(), методы создания фильтров специального вида);
- добавить созданный экземпляр фильтра в коллекцию фильтров запроса (метод Add() коллекции).
По умолчанию все фильтры, добавляемые в коллекцию Filters, объединяются между собой логической операцией AND. Свойство LogicalOperation коллекции Filters позволяет пользователю указать логическую операцию, которой необходимо объединять фильтры. Свойство принимает значения из перечисления LogicalOperationStrict.
В запросах EntitySchemaQuery реализована возможность управления фильтрами, участвующими в построении результирующего набора данных. Каждый элемент коллекции Filters имеет свойство IsEnabled, которое определяет, участвует ли данный элемент в построении результирующего запроса (true – участвует, false – не участвует). Аналогичное свойство IsEnabled также определено для всей коллекции Filters. Установив это свойство в false, можно полностью отключить фильтрацию для запроса, при этом коллекция фильтров запроса останется неизменной. Таким образом, изначально сформировав коллекцию фильтров запроса, в дальнейшем можно использовать различные комбинации для фильтрации этого запроса, не внося изменений в саму коллекцию. Пример управления фильтрами в запросе приведен ниже (пример 7).
Пример 7
// Создание экземпляра запроса с корневой схемой "City". var esqCities = new EntitySchemaQuery(UserConnection.EntitySchemaManager, "City"); esqCities.AddColumn("Name"); // Создание экземпляра первого фильтра. var esqFirstFilter = esqCities.CreateFilterWithParameters(FilterComparisonType.Equal, "Name", "Киев"); // Создание экземпляра второго фильтра. var esqSecondFilter = esqCities.CreateFilterWithParameters(FilterComparisonType.Equal, "Name", "Москва"); // Фильтры в коллекцию фильтров запроса будут объединяться логическим оператором OR. esqCities.Filters.LogicalOperation = LogicalOperationStrict.Or; // Добавление созданных фильтров в коллекцию запроса. esqCities.Filters.Add(esqFirstFilter); esqCities.Filters.Add(esqSecondFilter); // В данную коллекцию попадут объекты - результаты запроса, отфильтрованные по двум фильтрам. var entities = esqCities.GetEntityCollection(UserConnection); // Для второго фильтра указывается, что он не будет участвовать в построении результирующего запроса. // При этом данный фильтр не удаляется из коллекции фильтров запроса. esqSecondFilter.IsEnabled = false; // Обновляет экземпляр Select, ассоциированный с запросом, в соответствии с актуальным набором фильтров. esqCities.ResetSelectQuery(); // В данную коллекцию попадут объекты - результаты запроса, отфильтрованные только по первому фильтру. var entities1 = esqCities.GetEntityCollection(UserConnection);
Формирование путей колонок в фильтрах EntitySchemaQuery осуществляется в соответствии с общими правилами построения путей к колонкам относительно корневой схемы (описано выше).
Ниже приведен пример, демонстрирующий, как из корневой схемы ActivityCategory по обратным связям выбрать результаты активностей для конкретной категории. Для демонстрации возможностей использования обратных связей для колонок в фильтрах, для выбора конкретной активности будет использована не непосредственно колонка Id корневой схемы, а колонка – идентификатор активности, построенная по обратным связям через схему ActivityCategoryResultEntry.
Пример 8
// Создание экземпляра запроса с корневой схемой ActivityCategory. var esqResult = new EntitySchemaQuery(UserConnection.EntitySchemaManager, "ActivityCategory"); // Добавление в запрос по обратным связям колонки с результатом активности. esqResult.AddColumn("[ActivityCategoryResultEntry:ActivityCategory].ActivityResult"); // Определение идентификатора активности, для которой будут выбираться результаты. var requiredActivityCategoryId = new Guid("42C74C49-58E6-DF11-971B-001D60E938C6"); // Создание экземпляра фильтра для выбора результатов для конкретной категории активности. var filter = esqResult.CreateFilterWithParameters(FilterComparisonType.Equal, "[ActivityCategoryResultEntry:ActivityCategory].ActivityCategory.Id", requiredActivityCategoryId); // Добавление фильтра в коллекцию фильтров запроса. esqResult.Filters.Add(filter); // Текст результирующего sql-запроса (MS SQL): // SELECT // [ActivityCate1].[ActivityResultId] [ActivityCate1.ActivityResultId], // [ActivityResult].[Name] [ActivityResult.Name] // FROM // [dbo].[ActivityCategory] [ActivityCategory] // LEFT OUTER JOIN [dbo].[ActivityCategoryResultEntry] [ActivityCate1] // ON ([ActivityCate1].[ActivityCategoryId] = [ActivityCategory].[Id]) // LEFT OUTER JOIN [dbo].[ActivityResult] [ActivityResult] // ON ([ActivityResult].[Id] = [ActivityCate1].[ActivityResultId]) // WHERE // EXISTS ( // SELECT // [ActivityCategory1].[Id] [Id] // FROM // [dbo].[ActivityCategory] [ActivityCategory1] // WHERE // [ActivityCate1].[ActivityCategoryId] = [ActivityCategory1].[Id] // AND [ActivityCategory1].[Id] = '{42C74C49-58E6-DF11-971B-001D60E938C6}')