Delve 0.9.0-alpha

This is a prerelease version of Delve.
There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package Delve --version 0.9.0-alpha
NuGet\Install-Package Delve -Version 0.9.0-alpha
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="Delve" Version="0.9.0-alpha" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Delve --version 0.9.0-alpha
#r "nuget: Delve, 0.9.0-alpha"
#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 Delve as a Cake Addin
#addin nuget:?package=Delve&version=0.9.0-alpha&prerelease

// Install Delve as a Cake Tool
#tool nuget:?package=Delve&version=0.9.0-alpha&prerelease

Delve

Delve is a simple framework for ASP.NET Core that adds easy pagination, filtering, sorting, selecting and expanding to an MVC project without being tightly coupled to an ORM. Delve automatically adds an X-Pagination header to the response to allow for easy navigation through the pages.

Usage

I included a demo project that shows all the capabilites of this library in the Demo/ directory.

1. Add Delve to the MVC Project

Just append .AddDelve() to your .AddMvc() call in your Startup.cs file.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().AddDelve();
}

If you wish to change the default configuration of Delve you can add DelveOptions as a parameter to .AddDelve().

services.AddMvc().AddDelve(options => options.MaxPageSize = 15);

2. Add a QueryValidator to your Domain class

By deriving from AbstractQueryValidator<TDomain> you can precisely define what is allowed to by queried by the user. By adding virtual properties you do not have to create a new property for something you only want to expose to the API (See examples below).

using Delve.Validation;

public class User
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; set; }
    //"UserRoles" not shown here for brevity, check the demo project.
    public IEnumerable<UserRoles> { get; set; }
}

public class UserQueryValidator : AbstractQueryValidator<User>
{
    public UserQueryValidator()
    {
        //Adds selecting/filtering/ordering with key="Id" for Id property
        AddSelect("Id", x => x.Id);
        AddFilter("Id", x => x.Id);
        AddOrder("Id", x => x.Id);
        
        //Adds a virtual property for the Age of the user calculated using the DateOfBirth
        //By using AllowAll() you can automatically add filtering/sorting and selecting for a property
        AllowAll("Age", x => Math.Round((DateTime.Now - x.DateOfBirth).TotalDays / 365, 2));
        
        //Adds a virtual property with key="Name" for the combination of Last- and FirstName
        AllowAll("Name", x => x.LastName + "" + x.FirstName);
        
        //Allows you to use "Include" in ORM's like EFCore
        //For now this is an experimental feature since it does not allow a **.ThenInclude()** yet
        //So be careful when using this, it can lead to bad performance (i.e. UserRoles is a big table)
        Expand("UserRoles", x => x.UserRoles);
    }
}

3. Configure your Controller

By simply adding a IResourceParameter<TDomain> to the method signature Delve will automatically parse and validate the client request and upon an invalid request return a 400 BadRequest with a matching error message.

using Delve.Models;
using Delve.AspNetCore;

public class UserController : Controller
{
    private readonly IUrlHelper _urlHelper;
    public UserController(IUrlHelper urlHelper)
    {
        _urlHelper = urlHelper;
    }
    
    public async Task<IActionResult> GetUsers(IResourceParameter<User> parameter)
    {
        var collection = Context.Set<User>()
                //Applies expands to the IQueryable<User>. Since delve isn't directly coupled to EFCore
                //you need to pass in the include method as a delegate.
                .ApplyIncludes((q, i) => q.Include(i), parameters)
                //Applies filters to the IQueryable<User>
                .ApplyFilters(parameters)
                //Applies sorts to the IQueryable<User>
                .ApplyOrderBy(parameters);
                
        //ToPagedResultAsync() will pull the matching IQueryable data from the database and applies pagination.
        //Since System.Linq doesn't provide a way to asynchronously Count() and ToList()
        //you will need to pass in the matching methods of your ORM for the pagination to work.
        //If you dont need async capabilities you can use ToPagedResult(). It will work without any delegates.
        var users =  await collection.ToPagedResultAsync(
        
                        async q => await q.CountAsync(), 
                        async q => await q.ToListAsync(), parameters);
                        
        //Adds paginationheader to the response
        this.AddPaginationHelper(parameter, users, _urlHelper);
        
        return Ok(users.ShapeData(parameter));
}

4. Send a request

Filters:

There are a couple of ways you can work with Delve's filters. By adding filter= to the query string you can filter on any in the QueryValidator defined virtual properties. You can add mulitple filters by separating these with commas (i.e. filter=Id== 5, Name==John). Furthermore you can define logical OR behaviour by separating the values of one filter with a '|' operator. This way you can check for a user called "John" or "Mary" (i.e. filter=Name==John|Mary).

FilterOperators

Operator Interpretation
== Equal
==* CaseInsensitive Equal
!= NotEqual
!=* CaseInsensitive NotEqual
> GreaterThan
< LessThan
>= GreaterThanOrEqual
<= LessThanOrEqual
? Contains
?* CaseInsensitive Contains
^ StartsWith
^* CaseInsensitive StartsWith
$ EndsWith
$* CaseInsensitive EndsWith
Sorting

Just as Filtering, Sorting is delimited by commas, though unlike with Filtering order of the sorts matter. Meaning, if you have (orderby=Name, -Age) in your query, it will first order by Name ascending and then by Age descending (Linq equivalent: .OrderBy(x ⇒ x.Name).ThenByDescending(x ⇒ x.Age);.

Selecting

If your entity has numerous columns and as a consumer of the API you are only really interested in a couple of things you can save bandwith by using Select. Just like before selects are delimited by commas and allow you to select on any virtual property defined in the QueryValidator.

Request Example:
GET /api/Users
?filter=        Age>=20, Name$*Smith|Bullock
&orderby=       -Age, Name
&select=        Id, Name, Age
&expand=        UserRoles
&pageNumber=    1
&pageSize=      10
X-Pagination Response Header:
{   
    "currentPage":1,
    "pageSize":5,
    "totalPages":4,
    "totalCount":20,
    "previousPageLink":null,
    "nextPageLink": "{address}/api/Users?pageNumber=1
                                        &pageSize=10
                                        &filter=Age>=20,Name$*Smith|Bullock
                                        &orderby=-Age,Name
                                        &select=Id,Name,Age
                                        &expand=UserRoles"
}

Licensing

Delve is licensed under MIT.

Contribution

This project is still at an early stage in development so any contributions are welcomed! Even if it is just a suggestions/discussions about how to improve upon Delve!

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.
  • .NETStandard 2.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Delve:

Package Downloads
Delve.AspNetCore

Asp.NET Core MVC integration for the Delve library.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.9.6-alpha 1,002 3/5/2018
0.9.5.1-alpha 883 3/2/2018
0.9.5-alpha 903 3/1/2018
0.9.4-alpha 857 2/25/2018
0.9.3-alpha 867 2/17/2018
0.9.2-alpha 826 2/16/2018
0.9.1-alpha 894 2/16/2018
0.9.0-alpha 932 2/15/2018

Initial Release