Go to file
2018-01-28 09:20:51 +10:00
Sieve nuget package and custom operators 2018-01-28 09:20:51 +10:00
SieveTests nuget package and custom operators 2018-01-28 09:20:51 +10:00
.gitattributes Add .gitignore and .gitattributes. 2018-01-26 14:17:35 +10:00
.gitignore Add .gitignore and .gitattributes. 2018-01-26 14:17:35 +10:00
LICENSE Create LICENSE 2018-01-27 18:12:21 +10:00
README.md Fix send-a-request url 2018-01-27 21:51:00 +10:00
Sieve.sln Base & test solution 2018-01-27 09:26:37 +10:00

🎛️ Sieve

Sieve is a simple and extensible framework for .NET Core that adds sorting, filtering, and pagination functionality out of the box. Most common use case would be for serving ASP.NET Core GET queries.

Usage for ASP.NET Core

In this example, consider an app with a Post entity. We'll use Sieve to add sorting, filtering, and pagination capabilities when GET-ing all available posts.

1. Add required services (SieveProcessor<TEntity>)

Inject the SieveProcessor<TEntity> service for each entity you'd like to use Sieve with. So to use Sieve with Posts, go to ConfigureServices in Startup.cs and add:

services.AddScoped<SieveProcessor<Post>>();

2. Add Sieve attributes on properties you'd like to sort/filter in your models

Sieve will only sort/filter properties that have the attribute [Sieve(CanSort = true, CanFilter = true)] on them (they don't have to be both true). So for our Post entity model:

public int Id { get; set; }

[Sieve(CanFilter = true, CanSort = true)]
public string Title { get; set; }

[Sieve(CanFilter = true, CanSort = true)]
public int LikeCount { get; set; }

[Sieve(CanFilter = true, CanSort = true)]
public int CommentCount { get; set; }

[Sieve(CanFilter = true, CanSort = true, name = "created")]
public DateTimeOffset DateCreated { get; set; } = DateTimeOffset.UtcNow;

There is also the name parameter that you can use to have a different name for use by clients.

3. Use SieveModel in your controllers

In the action handling returning Posts, use the SieveModel to get the sort/filter/paginate query. Apply it by to your data by injecting SieveProcessor<Post> into the controller and using its ApplyAll method. For instance:

[HttpGet]
public JsonResult GetPosts(SieveModel sieveModel) 
{
    var result = _dbContext.Posts.AsNoTracking(); // Makes read-only queries faster
    result = _sieveProcessor.ApplyAll(sieveModel, result); // Returns `result` after applying the sort/filter/paginate query in `SieveModel` to it
    return Json(result.ToList());
}

There are also ApplySorting, ApplyFiltering, and ApplyPagination methods.

4. Send a request

Send a request

Add custom sort/filter methods

If you want to add custom sort/filter methods, inject ISieveCustomSortMethods<TEntity> or ISieveCustomFilterMethods<TEntity> with the implementation being a class that has custom sort/filter methods for TEntity.

For instance:

services.AddScoped<ISieveCustomSortMethods<Post>, SieveCustomSortMethodsOfPosts>();
services.AddScoped<ISieveCustomFilterMethods<Post>, SieveCustomFilterMethodsOfPosts>();

Where SieveCustomSortMethodsOfPosts for example is:

public class SieveCustomSortMethodsOfPosts : ISieveCustomSortMethods<Post>
{
    public IQueryable<Post> Popularity(IQueryable<Post> source, bool useThenBy, bool desc)
    {
        var result = useThenBy ?
            ((IOrderedQueryable<Post>)source).ThenBy(p => p.LikeCount) :
            source.OrderBy(p => p.LikeCount)
            .ThenBy(p => p.CommentCount)
            .ThenBy(p => p.DateCreated);

        return result;
    }
}

And SieveCustomFilterMethodsOfPosts:

public class SieveCustomFilterMethodsOfPosts : ISieveCustomFilterMethods<Post>
{
    public IQueryable<Post> IsNew(IQueryable<Post> source)
    {
        var result = source.Where(p => p.LikeCount < 100 &&
                                        p.CommentCount < 5);

        return result;
    }
}

Configure Sieve

Use the ASP.NET Core options pattern with SieveOptions to tell Sieve where to look for configuration. For example:

services.Configure<SieveOptions>(Configuration.GetSection("Sieve"));

Then you can add configuration:

{
    "Sieve": {
        "CaseSensitive": `boolean: should property names be case-sensitive? Defaults to false`,
        "DefaultPageSize": `number: optional number to trim to when no page argument is given`
    }
}

Send a request

With all the above in place, you can now send a GET request that includes a sort/filter/paginate query. An example:

GET /GetPosts

?sorts=     LikeCount,CommentCount,-created         // sort by likes, then comments, then descendingly by date created 
&filters=   LikeCount>10, Title@=awesome title,     // filter to posts with more than 10 likes, and a title that contains the phrase "awesome title"
&page=      1                                       // get the first page...
&pageSize=  10                                      // ...which contains 10 posts

More formally:

  • sorts is a comma-delimited ordered list of property names to sort by. Adding a - before the name switches to sorting descendingly.
  • filters is a comma-delimited list of {Name}{Operator}{Value} where
    • {Name} is the name of a property with the Sieve attribute or the name of a custom filter method for TEntity
    • {Operator} is one of the Operators (not used when using a custom method)
    • {Value} is the value to use for filtering (not used when using a custom method)
  • page is the number of page to return
  • pageSize is the number of items returned per page

Notes:

  • Don't forget to remove commas from any {Value} fields
  • You can have spaces anywhere except within {Name} or {Operator} fields

Creating your own DSL

You can replace this DSL with your own (eg. use JSON instead) by implementing an ISieveModel. You can use the default SieveModel for refrence.

Operators

Operator Meaning
== Equals
!= Not equals
> Greater than
< Less than
>= Greater than or equal to
<= Less than or equal to
@= Contains
_= Starts with