Функции для наложения ограничений можно строить с помощью SQLWhereLanguageDef или ExternalLangDef, однако они имеют довольно многословный синтаксис.
Чтобы упростить код построения функций ограничений, был разработан FunctionBuilder
- обертка над ExternalLangDef
, предоставляющая набор методов для построения функций ограничения. Эти методы являются аналогами метода ICSSoft.STORMNET.FunctionalLanguage.FunctionalLanguageDef.GetFunction(string, params object[));
. Однако тип функции закодирован в самом имени метода, а описания переменных VariableDef формируются на основе передаваемых строковых путей к свойствам. Кроме того, для многих функций реализованы generic-варианты, позволяющие избежать ошибок при указании ограничиваемого свойства.
FunctionBuilder
поддерживает также наложение ограничений на детейлы с использованием функции Exist.
FunctionBuilder
поддерживает не все множество функций, доступных для использования в GetFunction
, однако большую его часть, что позволяет прикладному разработчику использовать его для построения функций ограничения в большинстве ситуаций, тем самым уменьшая громоздскость кода. При необходимости можно реализовать дополнительные методы FunctionBuilder по аналогии с уже реализованными.
Пример
Представим себе модель данных, в которой есть сущность Документ (Document) и сущность СвязьДокументов (DocumentLink), имеющая две ссылки на Документ с наименованиями Document и LinkedDocument, а также ссылку на объект ТипСвязиДокументов (DocumentLinkType).
Предположим, нам необходимо вычитать из БД все объекты DocumentLink с определенным наименованием типа связи, связывающие два документа, и при этом не учитывать порядок, в котором фигурируют заданные документы в объекте СвязьДокументов. Таким образом мы должны получить ограничение, примерно соответствующее следующему sql-запросу:
SELECT *
FROM DocumentLink
JOIN DocumentLinkType ON DocumentLinkType.PrimaryKey = DocumentLink.DocumentLinkType
WHERE DocumentLinkType.Name = @typeName
AND ((Document = @document1 AND LinkedDocument = @document2) OR (Document = @document2 AND LinkedDocument = @document1))
Функция ограничения с использованием langdef
var lcs = LoadingCustomizationStruct.GetSimpleStruct(typeof(DocumentLink), DocumentLink.Views.DocumentLinkE);
lcs.LimitFunction = langdef.GetFunction(langdef.funcAND,
langdef.GetFunction(langdef.funcEQ, new VariableDef(langdef.StringType, "DocumentLinkType.Name"), typeName),
langdef.GetFunction(langdef.funcOR,
langdef.GetFunction(langdef.funcAND,
langdef.GetFunction(langdef.funcEQ, new VariableDef(langdef.GuidType, "Document"), document1.__PrimaryKey),
langdef.GetFunction(langdef.funcEQ, new VariableDef(langdef.GuidType, "LinkedDocument"), document2.__PrimaryKey)),
langdef.GetFunction(langdef.funcAND,
langdef.GetFunction(langdef.funcEQ, new VariableDef(langdef.GuidType, "Document"), document2.__PrimaryKey),
langdef.GetFunction(langdef.funcEQ, new VariableDef(langdef.GuidType, "LinkedDocument"), document1.__PrimaryKey))));
Функция ограничения с использованием FunctionBuilder
var lcs = LoadingCustomizationStruct.GetSimpleStruct(typeof(DocumentLink), DocumentLink.Views.DocumentLinkE);
lcs.LimitFunction = FunctionBuilder.BuildAnd(
FunctionBuilder.BuildEquals<DocumentLink>(x => x.DocumentLinkType.Name, typeName),
FunctionBuilder.BuildOr(
FunctionBuilder.BuildAnd(
FunctionBuilder.BuildEquals<DocumentLink>(x => x.Document, document1),
FunctionBuilder.BuildEquals<DocumentLink>(x => x.LinkedDocument, document2)
),
FunctionBuilder.BuildAnd(
FunctionBuilder.BuildEquals<DocumentLink>(x => x.Document, document2),
FunctionBuilder.BuildEquals<DocumentLink>(x => x.LinkedDocument, document1)
)
)
);
Как можно видеть из примера, код построения функции с использованием FunctionBuilder очень похож, но становится читаемее за счет меньшего количества аргументов у методов, а также отсутствия необходимости создавать VariableDef вручную в большинстве случаев. Кроме того, в примере с FunctionBuilder-ом
нельзя допустить ошибку в пути свойства, поскольку используется generic-метод, контролирующий имеющиеся в объекте свойства. Аналогичный по возможностям код можно реализовать и в случае использования GetFunction
с использованием метода Information.ExtractPropertyPath<T>()
, однако это сделает код еще многословнее.
Исходный код
С исходным кодом FunctionBuilder
и вспомогательных классов можно ознакомиться в репозитории ORM. Все методы покрыты тестовыми сценариями, с кодом которых также можно ознакомиться в репозитории ORM.
Методы, доступные в FunctionBuilder
Список методов, перечисленных здесь, может со временем измениться, поэтому за полным списком возможностей рекомендуется обратиться к автодокументации.