DynamicFilter allows to use essential linq filtering methods in web api apps.
All operations were tested against EF Core and PostgreSql.
Model:
classProduct{publicGuidId{get;set;}publicstringName{get;set;}publicdecimalPrice{get;set;}publicboolIsInStock{get;set;}publicboolIsForSale{get;set;}publicDateTimeExpireDate{get;set;}}Controller:
[Route("[controller]")]publicclassProductsController:ControllerBase{privatereadonlyAppDbContext_dbcontext;publicProductsController(AppDbContextdbcontext){_dbcontext=dbcontext;}[HttpPost("filter")]publicIActionResultGetFiltered([FromBody]Operation[]filter){returnOk(_dbcontext.Set<Product>().ApplyDynamicFilter(filter));}}Filter:
[
{
"name": "where",
"arguments": {
"conditions": [
{
"field": "Name",
"operator": "Contains",
"value": ["Snickers"]
},
{
"logic": "And",
"field": "IsForSale",
"operator": "Equals",
"value": ["true"]
}
],
"groups": []
}
},
{
"name": "distinct"
},
{
"name": "orderBy",
"arguments": "ExpireDate"
},
{
"name": "orderByDescending",
"arguments": "Price"
},
{
"name": "thenBy",
"arguments": "IsInStock"
},
{
"name": "thenByDescending",
"arguments": "IsForSale"
},
{
"name": "skip",
"arguments": 1
},
{
"name": "take",
"arguments": 2
},
{
"name": "select",
"arguments": ["Id", "Name"]
}
]Under the hood filter transforms into call
_dbcontext.Set<Product>().Where(x =>x.Name.Contains("Snickers")&&x.IsForSale).Distinct().OrderBy(x =>x.ExpireDate).OrderByDescending(x =>x.Price).ThenBy(x =>x.IsInStock).ThenByDescending(x =>x.IsForSale).Skip(1).Take(2).Select(x =>newDictionary<string,object>(){{"Id",x.Id},{"Name",x.Name}});Such implementation of Select was made due to the limitations of anonymous types and should be used as the last operation and only to reduce the final number of properties to be fetched from the database.
At this point following operators are available to use in Where predicate:
Equals
NotEqual
Any
Greater
GreaterOrEqual
Less
LessOrEqual
Exists
NotExists
StartsWith
EndsWith
Contains
NotContains
.Where(x =>((x.Name.EndsWith("Mars")||["Snickers","Mars"].Contains(x.Name))&&x.ExpireDate>=DateTime.UtcNow)&&(x.IsForSale||x.IsInStock))[
{
"name": "where",
"arguments": {
"conditions": [
{
"field": "Name",
"operator": "EndsWith",
"value": ["Mars"]
},
{
"logic": "Or",
"field": "Name",
"operator": "Any",
"value": ["Snickers", "Mars"]
},
{
"logic": "And",
"field": "ExpireDate",
"operator": "GreaterOrEqual",
"value": ["2023-02-13 17:56:19Z"]
},
{
"logic": "And",
"field": "IsForSale",
"operator": "Equals",
"value": ["true"]
},
{
"logic": "Or",
"field": "IsInStock",
"operator": "Equals",
"value": ["true"]
}
],
"groups": [
{
"start": 1,
"end": 2,
"level": 1
},
{
"start": 1,
"end": 3,
"level": 2
},
{
"start": 4,
"end": 5,
"level": 2
}
]
}
}
].Select(x =>x.Price).Distinct().OrderBy(x =>x)[
{
"name": "select",
"arguments": "Price"
},
{
"name": "distinct"
},
{
"name": "orderBy"
}
]