This is preliminary documentation and is subject to change.

Introduction

The DotNetExtras.OData library can parse and validate OData filter expressions. For the detailed description of the library API, code samples, and usage scenarios, see the API documentation section.

Overview

The DotNetExtras.OData library can be useful if your application needs to parse and/or validate OData filter expressions. While the parsing functionality is mostly for demonstration purposes, the validation functionality can be used to ensure that the filter expressions are valid before they are sent to a custom OData service.

Implementation

OData filter expressions apply data types. For example, if an application implements a REST API that supports OData filters to allow callers to search for users based on some criteria, then the filter should apply to the data type implementing the user or a subset of user properties that should be allowed in the filter query.

To validate the OData filter query before processing it or passing it to another service, the application can use the ODataFilterValidator class which will check the filter query against the basic OData filter query syntax. ODataFilterValidator will also make sure that only properties defined for the filter-specific type are used in the expression. The validator also supports custom rules that can further restrict the filter query to only allow certain properties or values.

The validation rules can be used to enforce:

  • Allowed operators and the minimum and maximum number each operator can be used.

  • Allowed properties and the minimum and maximum number of times each property can be used.

  • Allowed operators that can be used with each property and the minimum and maximum number of times each operator can be used with each property.

To validate a filter expression, pass the value to the ODataFilterValidator constructor class and check the Passed or Failed property. If validation fails, the Errors property will contain a list of errors describing the issues with the filter expression.

Rules

The validation rules can be specified via:

  1. An ODataFilterRules object.

  2. A JSON string representing a serialized instance of a ODataFilterRules object.

  3. A shortcut notation representing the rules.

Let's dissect the shortcut notation because it may not be obvious and it seems like the best option for use in the applications.

Syntax

The shortcut notation allows you to specify the rules in a concise way. The syntax is as follows:

  • Multiple rules are separated by the pipe (|) characters.

  • Each rule defines the explicit restrictions applied to one of the following categories:

    • Operators

    • Properties

    • Operators applied to a property

  • If no rule is defined for a particular category, all possible entries of this category will be allowed. If at least one rule is defined for a particular category, then only explicitly defined members of the category will be allowed. For example, if a rule only mentions two properties, then only these two properties will be allowed in the expression; however, all operators will be allowed.

  • The minimum and maximum number of occurrences in the filter expression can be defined after a colon (:) and separated by a comma (,), e.g. eq:1,2 (require the eq operator to be used in the expression at least once and at most two times). Each number is optional and can be omitted, e.g. eq:1, (require the eq operator to be used in the expression at least once) or eq:,1 (allow the eq operator to be used in the expression at most once; since there is no minimum number, it is not required).

  • To add a restriction for an operator that should be allowed to be used with a property, put the name of the operator inside of the square brackets ([]) after the property name (the minimum and maximum occurrence numbers will apply to the number of times the operator is used with the property, e.g. type[eq]:1,1 (require the filter to include the type eq expression exactly once).

  • It should never be the case, but if the name of a property conflicts with the name of an OData operator, a prefix hint (o: for an operator or p: for a property) must be used in the rule, e.g. p:type:1,1|o:eq:1,1.

Examples

The following examples show how to validate OData filter expressions.

Validate filter expression without rules and print errors
using DotNetExtras.OData;
...

// Just validate the basic syntax of the filter expression.
ODataFilterValidator<User> validator = new("user/loginId eq 'test'");

if (validator.Failed)
{
    Console.WriteLine(string.Join(" ", validator.Errors));
}
Validate filter expression with rules and print errors
using DotNetExtras.OData;
...

// Validate the syntax of the filter expression and enforce the 'startsWith(loginId, ...)' query.
ODataFilterValidator<User> validator = new("startsWith(loginId, 'test')", "loginId[startsWith]:1,1");

if (validator.Failed)
{
    Console.WriteLine(string.Join(" ", validator.Errors));
}

Shortcuts

The following examples illustrate the formats of the filter validation rules in the shortcut forms:

Explicit restrictions for operators (no min/max)
eq|startsWith|and

Allowed operators: eq, startsWith, and. Any property can be used.

Explicit restrictions for properties (no min/max)
email|address/country|phone/countryPrefix

Allowed properties: email, address.country, phone.countryPrefix.

Explicit restrictions for operators and properties (with min/max)
eq:,1|startsWith:,1|and:,1|loginId:1,1|givenName:,1

Allowed operators: eq, startsWith, and (each can be used at most once). Required property: loginId (must be used once). Optional property: givenName (can be used at most once).

Explicit restrictions for operators allowed to be used with properties (with and without min/max)
type[eq]:1,1|type[and]:1,1|name[eq]:,1|name[startsWith]:,1|name[and]:,1|loginId[eq,and]:,2|givenName[eq,startsWith]:,2

Required expression: type eq ... and. Allowed expressions: name eq ..., startsWith(name, ...), loginId eq ..., givenName eq ... (each can be used at most once). Optional expressions can be combined using the logical operator and. Notice that when combining multiple operators for one property, the maximum number must be adjusted to accommodate both counts.

More

For the complete example and other samples covering additional scenarios, see the source code (and read the comments) of the filter parser and filter validator projects. More examples can be also found in the unit tests

See Also