Skip to content

PoweredSoft/DynamicQuery

Repository files navigation

Dynamic Query

It's a library that allows you to easily query a queryable using a criteria object.

It also offers, to intercept the query using IQueryInterceptor implementations.

Breaking Changes

If you are moving up from v1, the breaking changes details are lower.

Getting Started

Install nuget package to your awesome project.

Full Version NuGet NuGet Install
PoweredSoft.DynamicQuery NuGet PM> Install-Package PoweredSoft.DynamicQuery
PoweredSoft.DynamicQuery.Core NuGet PM> Install-Package PoweredSoft.DynamicQuery.Core
PoweredSoft.DynamicQuery.AspNetCore NuGet PM> Install-Package PoweredSoft.DynamicQuery.AspNetCore
PoweredSoft.DynamicQuery.AspNetCore.NewtonsoftJson NuGet PM> Install-Package PoweredSoft.DynamicQuery.AspNetCore.NewtonsoftJson

Using in ASP.NET Core

The package Asp.net core of dynamic query will help you start to use Dynamic Query faster in your web project.

Now Supports .NET 5

.NET Version Compatible Branch
.NET 5.0 3.0.0+
.NET 3.1.x 3.0.1 or 2.1
.NET 2.1.x 2.0

New in Since 2.1.3

You may now add a IQueryInterceptorProvider to return interceptors on top of being able to use AddInterceptor.

public interface IQueryInterceptorProvider
{
    IEnumerable<IQueryInterceptor> GetInterceptors<TSource, TResult>(IQueryCriteria queryCriteria, IQueryable<TSource> queryable);
}

How to configure during startup (NET Core 3)

using PoweredSoft.DynamicQuery.AspNetCore.NewtonsoftJson;

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services
            .AddMvc()
            .AddPoweredSoftJsonNetDynamicQuery();
    }
}

How to use in a controller

[HttpGet]
public IQueryExecutionResult<OfSomething> Get(
            [FromServices]YourContext context, 
            [FromServices]IQueryHandler handler, 
            [FromServices]IQueryCriteria criteria,
            int? page = null,
            int? pageSize = null)
{
    criteria.Page = page;
    criteria.PageSize = pageSize;
    IQueryable<OfSomething> query = context.Somethings;
    var result = handler.Execute(query, criteria);
    return result;
}

[HttpPost]
public IQueryExecutionResult<OfSomething> Read(
    [FromServices]YourContext context, 
    [FromServices]IQueryHandler handler,
    [FromBody]IQueryCriteria criteria)
{
    IQueryable<OfSomething> query = context.Somethings;
    var result = handler.Execute(query, criteria);
    return result;
}

New support for async

[HttpPost]
public async Task<IQueryExecutionResult<OfSomething>> Read(
    [FromServices]YourContext context, 
    [FromServices]IQueryHandlerAsync handler,
    [FromBody]IQueryCriteria criteria)
{
    IQueryable<OfSomething> query = context.Somethings;
    var result = await handler.ExecuteAsync(query, criteria);
    return result;
}

Sample Web Project - ASP.NET CORE + EF Core

Visit: https://github.com/PoweredSoft/DynamicQueryAspNetCoreSample

Breaking Changes if you are migrating from 1.x

Response interface, is now generic IQueryResult<T> which impacts the way to execute the handler.

Grouping results

Since the results are now generic, it's no longer a List in the response so that changes the result if grouping is requested.

You have now a property Groups, and HasSubGroups, and SubGroups.

QueryConvertTo Interceptor

If you are using IQueryConvertTo interceptors, it's new that you must specify the type you are converting to Ex:

IQueryable<OfSomething> query = context.Somethings;
var result = handler.Execute<OfSomething, OfSomethingElse>(query, criteria);

Criteria

Criteria must implement the following interfaces

Object Interface Implementation Example Description
Query Criteria interface default implementation test Wraps the query parameters
Paging interface default implementation test Paging support
Filter interface default implementation test Represent a filter to be executed
Simple Filter interface default implementation test Represent a simple filter to be executed
Composite Filter interface default implementation test Represent a composite filter to be executed
Sort interface default implementation test Represent a sort to be executed
Group interface default implementation test Represent a group to be executed
Aggregate interface default implementation test Represent an aggregate to be executed

Sample

var criteria = new QueryCriteria
{
    Page = 1,
    PageSize = 12,
    Filters = new List<IFilter>
    {
        new SimpleFilter { Path = "FirstName", Type = FilterType.Equal, Value = "John" }
    }
};

var queryHandler = new QueryHandler();
IQueryExecutionResult<OfSomeQueryableType> result = queryHandler.Execute(someQueryable, criteria);

Query Result

Here is the interfaces that represent the result of query handling execution.

Changed in 2.x

public interface IAggregateResult
{
    string Path { get; set; }
    AggregateType Type { get; set; }
    object Value { get; set; }
}

public interface IQueryResult<TRecord>
{
    List<IAggregateResult> Aggregates { get; }
    List<TRecord> Data { get; }
}

public interface IGroupQueryResult<TRecord> : IQueryResult<TRecord>
{
    string GroupPath { get; set; }
    object GroupValue { get; set; }
    bool HasSubGroups { get; }
    List<IGroupQueryResult<TRecord>> SubGroups { get; set; }
}

public interface IQueryExecutionResultPaging
{
    long TotalRecords { get; set; }
    long? NumberOfPages { get; set; }
}

public interface IQueryExecutionResult<TRecord> : IQueryResult<TRecord>, IQueryExecutionResultPaging
{

}

public interface IQueryExecutionGroupResult<TRecord> : IQueryExecutionResult<TRecord>
{
    List<IGroupQueryResult<TRecord>> Groups { get; set; }
}

Interceptors

Interceptors are meant to add hooks at certain part of the query handling to allow alteration of the criterias or the queryable it self.

The following is documented in the order of what they are called by the default query handler implementation.

Before the expression is being built

Interceptor Interface Example Description
IIncludeStrategyInterceptor interface test This is to allow you to specify include paths for the queryable
IIncludeStrategyInterceptor<T> interface test This is to allow you to specify include paths for the queryable
IBeforeQueryFilterInterceptor interface test Before adding the filters to the expression
IBeforeQueryFilterInterceptor<T> interface test Before adding the filters to the expression
INoSortInterceptor interface test This is called to allow you to specify an OrderBy in case none is specified, to avoid paging crash with EF6
INoSortInterceptor<T> interface test This is called to allow you to specify an OrderBy in case none is specified, to avoid paging crash with EF6

After/During expression building before query execution

Interceptor Interface Example Description
IFilterInterceptor interface test This interceptor allows you to change the behavior of a IFilter being applied to the queryable
ISortInterceptor interface test This interceptor allows you to change the behavior of a ISort being applied to the queryable
IGroupInterceptor interface test This interceptor allows you to change the behavior of a IGroup being applied to the queryable
IAggregateInterceptor interface test This interceptor allows you to change the behavior of a IAggregate being applied to the queryable

Post Query execution

Interceptor Interface Example Description
IQueryConvertInterceptor interface test This interceptor allows you to replace the object that is being returned by the query, by another object instance
IQueryConvertInterceptor<T, T2> interface test This interceptor allows you to replace the object that is being returned by the query, by another object instance (restricts the source)
IQueryConvertInterceptor<T, T2> interface test This interceptor allows you to replace the object that is being returned by the query, by another object instance (restricts the source & output)