QueryX 2.1.1

dotnet add package QueryX --version 2.1.1
NuGet\Install-Package QueryX -Version 2.1.1
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="QueryX" Version="2.1.1" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add QueryX --version 2.1.1
#r "nuget: QueryX, 2.1.1"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install QueryX as a Cake Addin
#addin nuget:?package=QueryX&version=2.1.1

// Install QueryX as a Cake Tool
#tool nuget:?package=QueryX&version=2.1.1

QueryX

QueryX allows performing filtering, paging and sorting to IQueryable ends using URL query strings.

Installation

Install with nuget:

Install-Package QueryX

Usage

A filter model is needed to define the properties that will be used for filtering, this model could be an specific filter class, a Dto or a Domain model object.

public class Card
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public int Priority { get; set; }
    public float EstimatedPoints { get; set; }
    public List<User> Owners { get; set; }
}

By default all properties can be used for filtering and sorting, this could be customized using fluent configuration.

Example

QueryX adds ApplyQuery extension methods to IQueryable interface to perform queries:

var filter = "priority > 1";
var result = _context.Set<Card>().ApplyQuery(filter).ToList();

Filtering

Filtering is made using operators, so a filter is defined this way: propertyName operator value.

It is possible to combine multiple filters using "and" (&) and "or" (|) logical operators:

id>1 & title=-'test' | priority|=1,2

For facilitating writing queries in URL, ; (semicolon) character can be used instead for representing the and logical operator:

id>1 ; title=-'test' | priority|=1,2

Filter grouping

Filters can be also grouped using parentheses to determine how they should be evaluated:

id>1 ; (title=-'test' | priority|=1,2)

Collection filters

It is possible to specify filters for collection properties with the following syntax:

propertyName(childPropertyName operator value)

The above code will use the Enumerable.Any method for applying the conditions.

For using the Enumerable.All method:

propertyName*(childPropertyName operator value)

An example using the Card object would be:

owners(id==1 | name=='user2')

Supported value types

Category Description
Numbers integer, float, real, etc.
String, Char should be wrapped in single quotes
DateTime, Timespan should be wrapped in single quotes
Enums should be wrapped in single quotes
Constants true, false, null

Operators

Operator Description Comment
== Equal operator
!= Not equal
< Less than
Less than or equal
> Greater than
>= Greater than or equal
-=- Contains String type only
=- Starts with String type only
-= Ends with String type only
|= In Allows multiple values

Multiple values are specified this way: 0,1,2 or 'val1','val2','val3' if the values are strings

Case insensitive operator

All operators can be combined with the case insensitive operator (*) for ignoring case when comparing strings:

title ==* 'TeSt VaLuE'

This operator is intended to work only with string properties

Not Operator

The not operator (!) can be applied to any filter, collection filter or group:

!id>1 ; !title=-'test' | !priority|=1,2
id>1 ; !(title=-'test' | priority|=1,2)
!owners(id==1 | name=='user2')

Sorting and Paging

The ApplyOrderingAndPaging method allow specifying ordering.

For ascending order base on property Title:

title

For descending order:

-title

It is possible to combine multiple orderings:

id,-priority,title

Example:

var sortBy = "-estimatedPoints";
var offset = 10;
var limit = 10;
_context.Set<Card>().ApplyOrderingAndPaging(sortBy, offset, limit);

Customize filtering and sorting

It is possible to customize the filtering behavior with QueryMappingConfig class:

QueryMappingConfig.Global
    .For<Card>(cfg => 
    {
        // mapping configurations
    });
Map property name:

Allows mapping a property with a different name that will appear in the filter or sortBy string:

cfg.Property(c => c.Priority).MapFrom("queryPriority");
cfg.Property(c => c.Board.Title).MapFrom("bTitle");

Additionally, MapFrom method supports a second parameter to apply custom conversion from string to the property type:

cfg.Property(c => c.Priority).MapFrom("queryPriority", value => int.ParseInt(value));
Ignore properties:

Ignore properties from filter and sorting steps. IgnoreFilter and IgnoreSort methods exists also.

cfg.Property(c => c.Priority).Ignore();
Custom filters:

This properties are excluded as part of the filter, custom code needs to be written for doing something with the filter values. Custom filters are applied after all filters have been applied

cfg.Property(c => c.Priority).CustomFilter((source, values, op) => 
{
    // source   : IQueryable instance to apply the custom filter
    // values   : values specified in filter string
    // op       : operator for this filter
    return source.Where(c => c.Priority == values[0]);
});
Custom sort:

This properties are excluded as part of the sort behavior, custom code needs to be written to apply them. Custom filters are applied after all filters have been applied

cfg.Property(c => c.Priority).CustomSort((source, ascending, isOrdered) => 
{
    // source   : IQueryable instance to apply the custom filter
    // ascending: indicates if the sorting should be done in ascending direction
    // isOrdered: indicates if the IQueryable instace have been already ordered, if true, ThenBy and ThenByDescending methods should be used as appropriate
    return isOrdered
        ? ascending
            ? source.ThenBy(c => c.Priority)
            : source.ThenByDescending(c => c.Priority)
        : ascending
            ? source.OrderBy(c => c.Priority)
            : source.OrderByDescending(c => c.Priority);
});
Concatenate configurations:

Configurations can be concatenated as required:

cfg.Property(c => c.Priority).MapFrom("queryPriority").Ignore();
cfg.Property(c => c.Board.Title).MapFrom("bTitle").CustomFilter((source, values, op) => 
{
    // ...
});

Override global configurations

The configurations created using QueryMappingConfig.Global are applied globally. ApplyQuery and ApplyOrderingAndPaging methods accepts a QueryMappingConfig instance in case specific configuration is needed:

var config = new QueryMappingConfig();
config.For<Card>(cfg =>
{
    // set specific configurations
});

var filter = "id |= 2,4,6";
_context.Set<Card>().ApplyQuery(filter, mappingConfig: config);

Extending global configuration is also possible:

var config = QueryMappingConfig.Global.Clone()
config.For<Card>(cfg =>
{
    // set specific configurations
});

var filter = "id |= 2,4,6";
_context.Set<Card>().ApplyQuery(filter, mappingConfig: config);

QueryModel

QueryModel is a class that can be used to capture user parameters in a WebAPI endpoint, it contains Filter, SortBy, Offset and Limit properties. ApplyQuery and ApplyOrderingAndPaging methods have overloads to receive a QueryModel instance.

Query exceptions

By default invalid properties will be ignored for filtering and ordering but it is possible to change this behavior by calling ThrowQueryExceptions() when registering QueryX:

builder.Services.AddQueryX(o => o.ThrowQueryExceptions(false));

These exceptions will be thrown as appropriate:

  • InvalidFilterPropertyException for filtering errors
  • InvalidOrderingPropertyException for ordering errors

It is also possible set this configuration using QueryMappingConfig:

QueryMappingConfig.Global
    .SetQueryConfiguration(options => options.ThrowQueryExceptions(true));
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
2.1.1 295 3/25/2024
2.1.0 488 12/7/2023
2.0.0 706 11/11/2023
2.0.0-beta.4 56 11/11/2023
2.0.0-beta.3 126 11/5/2023
2.0.0-beta.2 155 10/28/2023
2.0.0-beta.1 64 10/26/2023
2.0.0-alpha-02 69 10/22/2023
2.0.0-alpha-01 69 10/22/2023
1.2.0 210 9/16/2023
1.1.0 246 9/8/2023
1.0.2 615 5/3/2023
1.0.1 128 4/26/2023
1.0.0 390 10/6/2022
1.0.0-beta-0007 95 10/4/2022
1.0.0-beta-0006 144 9/28/2022
1.0.0-beta-0005 127 9/17/2022
1.0.0-beta-0004 171 9/14/2022
1.0.0-beta-0003 105 9/13/2022
1.0.0-beta-0001 99 9/10/2022
1.0.0-alpha-0002 98 9/9/2022
1.0.0-alpha-0001 92 9/8/2022