An AspNetCore quick and dirty OData-like replacement for queryable APIs. Works for standard POCO entities. No EDM hassles. No middleware configuration. However, this is not an OData parser. Although $top,$skip,$orderBy work just like OData syntax, $filter is not an OData AST parser. Instead it works by a custom string deserialization implementation for parsing by the System.Linq.Dynamic IQueryable extensions.
Install-Package RevStackCore.Extensions.DynamicLinq
The $filter querystring should be a stringified array of QueryFilterOptions
public class QueryFilterOptions
{
public FilterOperation Operation { get; set; }
public string Property { get; set; }
public object Value { get; set; }
public string Sql { get; set; }
public FilterTransform? Transform { get; set; }
}
public enum FilterOperation
{
Eq,
Ne,
Gt,
Ge,
Lt,
Le,
Contains,
StartsWith,
EndsWith,
Sql,
SqlUnion
}
public enum FilterTransform
{
Lower,
Upper,
Trim
}
The $where querystring allows you to instead pass a raw sql WHERE clause to be parsed by the System.Linq.Dynamic IQueryable extensions. The syntax must match the System.Linq.Dynamic convention. Hence, @0...@N must parametrize the values in the expression. The Parameter values are then passed through a corresponding $params querystring.
Ex: $where=Name==@0&$params=Bob
the $params querystring is a pipe-delimited("|") string of param values for the matching $where expression. $params is required if $where is set.
$params=value0|value1|...valueN
Ex: $where=Name==@0%26%26Email==@1&$params=Bob|[email protected]
Importing the library introduces two extension methods on the Http Request object.
IQueryable<TEntity> ApplyTo<TEntity>(this HttpRequest request, IQueryable<TEntity> query)
QueryResult<TEntity> PageResult<TEntity>(this HttpRequest request, IQueryable<TEntity> query)
QueryResult has the same structure as a returned OData PageResult
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using RevStackCore.Extensions.DynamicLinq;
namespace MyApp.Controllers
{
[Route("api/[controller]")]
public class MyEntityController : Controller
{
private readonly IService _service;
public MyEntityController(IService service)
{
_service = service;
}
[HttpGet("")]
public async Task<QueryResult<MyEntity>> Get()
{
var query = await _service.GetAsync();
query = query.AsQueryable();
return Request.PageResult(query);
}
}
}
// Filter an IQueryable using a string queryString
IQueryable<TEntity> ApplyTo<TEntity>(this string queryString, IQueryable<TEntity> query)
## Client-Side(Javascript)
```js
let endpoint='/api/MyEntity';
let filterModel=[{
property:'Name',
operation:'Contains',
transform:'Lower',
value:'bob'
}];
let $filter = JSON.stringify(filterModel);
$filter=encodeURIComponent($filter);
let query=endpoint + "?$filter=" + $filter + "&$top=100&$orderby='Name'";
fetch(query, {
method: 'get',
headers: new Headers({
'Content-Type': 'application/json'
})
})
.then(function(response){
//response
})
.catch(function(err){
//error
})
///using $where with $params
///E.g, filter on a Customer Orders IEnumerable property
/api/Customer?$where=Orders.Any(ZipCode==@0&&Phone==@1)&$params=10001|5555555555