AutoFilter simplifies data filtering using Expression Trees. Reduces the need for writing filtering logic on backend side.
Inspired by Max Arshinov's article.
All operations were tested against EF Core and PostgreSql.
Any issues or criticism are welcome :)
Create a model
classProduct{publicGuidId{get;set;}publicstringName{get;set;}publicdecimalPrice{get;set;}publicboolIsInStock{get;set;}publicboolIsForSale{get;set;}publicDateTimeExpireDate{get;set;}}Build a filter
varfilter=newAutoFilter(Filter:newFilterRule(Conditions:new[]{newCondition(Name:"Name",SearchOperator:SearchOperator.Contains,Value:new[]{"Snickers"}),newCondition(LogicOperator:LogicOperator.And,Name:"IsForSale",SearchOperator:SearchOperator.Equals,Value:new[]{"true"}),}));Apply filter to queryable
queryable.ApplyFilter(filter);Under the hood filter transforms into call
queryable.Where(x =>x.Name.Contains("Snickers")&&x.IsForSale);queryable.Where(x =>((x.Name.StartsWith("Snickers")||x.Name.Contains("Mars"))&&x.ExpireDate>=DateTime.UtcNow)&&(x.IsForSale||x.IsInStock))varfilter=newAutoFilter(Filter:newFilterRule(newCondition[]{new(Name:nameof(Product.Name),SearchOperator:SearchOperator.StartsWith,Value:new[]{"Snickers"}),new(LogicOperator:LogicOperator.Or,Name:nameof(Product.Name),SearchOperator:SearchOperator.Contains,Value:new[]{"Mars"}),new(LogicOperator:LogicOperator.And,Name:nameof(Product.ExpireDate),SearchOperator:SearchOperator.GreaterOrEqual,Value:new[]{DateTime.UtcNow.ToString("u")}),new(LogicOperator:LogicOperator.And,Name:nameof(Product.IsForSale),SearchOperator:SearchOperator.Equals,Value:new[]{"true"}),new(LogicOperator:LogicOperator.Or,Name:nameof(Product.IsInStock),SearchOperator:SearchOperator.Equals,Value:new[]{"true"}),},newGroup[]{new(Start:1,End:2,Level:1),new(Start:1,End:3,Level:2),new(Start:4,End:5,Level:2)}));queryable.ApplyFilter(filter);queryable.Distinct();varfilter=newAutoFilter(DistinctBy:"");queryable.ApplyFilter(filter);queryable.DistinctBy(x =>x.Name);varfilter=newAutoFilter(DistinctBy:"Name");queryable.ApplyFilter(filter);queryable.OrderBy(x =>x.ExpireDate);varfilter=newAutoFilter(Sorting:newSortingRule(PropertyName:"ExpireDate"));queryable.ApplyFilter(filter);queryable.OrderBy(x =>x.ExpireDate).ThenByDescending(x =>x.Price);varfilters=newAutoFilter[]{new(Sorting:newSortingRule(PropertyName:"ExpireDate")),new(Sorting:newSortingRule(PropertyName:"Price",ThenBy:true,IsDescending:true)),};queryable.ApplyFilters(filters);queryable.Skip(5).Take(1);varfilters=new[]{newAutoFilter(Skip:5),newAutoFilter(Top:1)};queryable.ApplyFilters(filters);queryable.Select(x =>newDictionary<string,object>(){{"Id",x.Id},{"Name",x.Name}})varfilter=newAutoFilter(Select:new[]{"Id","Name"});queryable.ApplyFilterAndSelect(filter);This 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.


