Gridify

repository·master·Indexed 22 days ago

https://github.com/alirezanet/gridify

A high-performance dynamic LINQ library that converts string-based queries into LINQ expressions for filtering, sorting, and pagination on server-side collections. It includes a .NET package and a JavaScript/TypeScript client (gridify-client v2.2.0) for front-end integration. Gridify is compatible with Entity Framework, AutoMapper, and Elasticsearch, providing tools like GridifyQueryBuilder for fluent query construction and support for compiled expressions to optimize in-memory collection performance.

Tokens
21.7K
Snippets
79
Records
92
Agent score
75%

What's inside Gridify

  1. Overview of Gridify features

    master

    Gridify is a dynamic LINQ library that converts strings into LINQ queries. It is designed for high performance and ease of use when applying data operations via text-based inputs.

    Key capabilities include:

    • Query Operations: Supports filtering, sorting, and pagination.
    • Complex Queries: Supports nested queries, sub-collections, and mapping from strings to objects.
    • Optimization: Supports query compilation for better performance.
    • Compatibility:
      • Works with any collection supporting LINQ.
      • Compatible with object-mappers like AutoMapper.
      • Compatible with ORMs, specifically Entity Framework.
      • Supports Elasticsearch DSL queries.
    • Ecosystem: Includes a Javascript/Typescript client called gridify-client.
  2. Overview of Gridify

    master

    Gridify is a modern dynamic LINQ library designed to simplify converting text-based strings into LINQ queries. It provides a high-performance alternative to GraphQL or OData by allowing front-end clients to selectively filter, sort, and paginate resources, requesting only the necessary records.

    Key capabilities include:

    • Filtering, Sorting, and Pagination: Apply these operations using simple string syntax.
    • Complex Query Support: Handles nested queries, sub-collections, and string-to-object mapping.
    • Performance: Supports query compilation and collection indexes.
    • Extensibility: Allows for custom operators.
    • Compatibility: Works with ORMs (especially Entity Framework), object-mappers (like AutoMapper), Elasticsearch, and any collection supported by LINQ.
  3. Understand the Paging<T> return type

    master

    When using the Gridify extension method for pagination, the result is wrapped in a Paging<T> object. This DTO is designed to provide both the current page of data and the metadata required for client-side pagination controls.

    public class Paging<T>
    {
        public int Count { get; set; } // Total number of records across all pages
        public IEnumerable<T> Data { get; set; } // The records for the current page
    }
  4. Configure filtering, ordering, and paging with GridifyQuery

    master

    The GridifyQuery class is used to encapsulate parameters for filtering, ordering, and paging data. You can instantiate it and set the Filter, Page, PageSize, and OrderBy properties. Once configured, you can apply these settings to a repository or collection using the .Gridify() extension method.

    var gq = new GridifyQuery()
    {
        Filter = "FirstName=John",
        Page = 1,
        PageSize = 20,
        OrderBy = "Age"
    };
    
    // Apply Filter, Sort and Paging
    Paging<Person> result = personsRepo.Gridify(gq);
  5. Configure GridifyQuery for Filtering, Paging, and Sorting

    master

    The GridifyQuery class is used to configure how data is filtered, sorted, and paginated. While often provided automatically by API controllers, you can instantiate it manually.

    Key properties:

    • Filter: A string representing the filtering conditions.
    • IsSortAsc: A boolean indicating if sorting should be ascending.
    • Page: The current page number.
    • PageSize: The number of items per page.
    • SortBy: The field name to sort by.
    var gQuery = new GridifyQuery()
    {
        Filter = "FirstName==John",
        IsSortAsc = false,
        Page = 1,
        PageSize = 20,
        SortBy = "LastName"
    };
    
    Paging<Person> pData = myDbContext.Persons.Gridify(gQuery);
    
    // pData.TotalItems => Count of persons matching the filter
    // pData.Items      => The subset of items for the requested page
  6. Gridify performance characteristics

    master

    Gridify is optimized for performance, performing nearly identically to native LINQ in modern .NET environments (such as .NET 10).

    Key Performance Notes:

    • Filtering: This is the most expensive operation in Gridify. While Gridify performs nearly as well as native LINQ, native LINQ performance has caught up due to improvements in the .NET runtime.
    • Pagination and Sorting: These operations have minimal performance impact.
    • Comparison: In benchmarks, Gridify significantly outperforms other dynamic LINQ libraries like Sieve, DynamicLinq, and Fop, and is vastly faster than CSharp_Scripting.
  7. Reuse mappings with AddNestedMapper

    master

    The AddNestedMapper method allows you to reuse GridifyMapper<T> configurations for nested objects within a QueryBuilder<T>. This enables DRY (Don't Repeat Yourself) composition of mappers. You can use a mapper instance directly or use a custom mapper class.

    Usage Patterns

    1. Reusing a mapper instance without a prefix

    When you pass a property selector without a string prefix, the mapper properties are merged directly into the root level of the query.

    2. Reusing a mapper instance with a prefix

    By providing a string prefix (e.g., "location"), you can access nested properties using dot notation (e.g., "location.city=Berlin").

    3. Using custom mapper classes

    You can pass a type parameter to AddNestedMapper<TMap> if you have defined a class inheriting from GridifyMapper<T>.

    // Example: Reusing Address Mapper with a prefix
    var addressMapper = new GridifyMapper<Address>()
        .AddMap("city", x => x.City)
        .AddMap("country", x => x.Country);
    
    var builder = new QueryBuilder<Company>()
        .AddMap("name", x => x.Name)
        .AddNestedMapper("location", x => x.Address, addressMapper)
        .AddCondition("location.city=Berlin")
        .ConfigurePaging(0, 10);
    
    var result = builder.Build(companies.AsQueryable());
  8. Use Gridify in ASP.NET Web APIs

    master

    Gridify is optimized for ASP.NET APIs. You can use GridifyQuery as a parameter in your controller actions to automatically handle filtering, sorting, and pagination from query strings.

    Example request URL: http://exampleDomain.com/api/GetPersons?pageSize=100&page=1&sortBy=FirstName&isSortAsc=false&filter=Age%3D%3D10

    // ApiController
    
    [Produces(typeof(Paging<Person>))]
    public IActionResult GetPersons([FromQuery] GridifyQuery gQuery)
    {
        // Gridify applies Filter, Sort & Apply Paging
        return myDbContext.Persons.Gridify(gQuery);
    }
  9. Compile and reuse Gridify expressions for high performance

    master

    To avoid the overhead of re-parsing filter strings, you can extract and reuse Gridify-generated expressions. You can access these expressions via GridifyQuery.GetFilteringExpression<T>() or QueryBuilder<T>.BuildFilteringExpression().

    For maximum performance, you should compile the expression into a delegate. This provides a massive performance boost compared to standard expression evaluation.

    Warning: Only use a compiled expression (delegate) if you are working with in-memory collections. Do not use compiled expressions when using Gridify with an ORM like Entity Framework, as ORMs require the uncompiled Expression<Func<T, bool>> to translate queries into SQL.

    // Using GridifyQuery
    var gq = new GridifyQuery() { Filter = "name=John" };
    var expression = gq.GetFilteringExpression<Person>();
    var compiledExpression = expression.Compile();
    var result = persons.Where(compiledExpression);
  10. Filter sub-collections, arrays, and dictionaries using indexers

    master

    Since version v2.15.0, Gridify supports filtering on indexable properties by specifying an index or key within square brackets [ ]. This requires defining mappings via GridifyMapper for the target properties.

    var gm = new GridifyMapper<TargetType>()
          .AddMap("arrayProp", (target, index) => target.MyArray[index].Prop)
          .AddMap("dictProp", (target, key) => target.MyDictionary[key]);
    
    var gq = new GridifyQuery
    {
        Filter = "arrayProp[8] > 10, dictProp[name] = John"
    };
  11. Install Gridify.Elasticsearch

    master

    To use Gridify with Elasticsearch, you must install the Gridify.Elasticsearch package. This package provides extension methods for the Elastic.Clients.Elasticsearch .NET client to convert Gridify filters, sortings, and paging into Elasticsearch DSL queries.

    # Package Manager
    Install-Package Gridify.Elasticsearch -Version {{ $version }}
    
    # .NET CLI
    dotnet add package Gridify.Elasticsearch --version {{ $version }}