I've created a nuget package Linq.Extension.PredicateBuilder helps you to solve complexity in filter collection by apply filtering on combination of properties.
Overview
To make the scenario concrete, let's assume that our object is declared as follows:
public class Post
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool IsPublished { get; set; }
public DateTime PublishedAt { get; set; }
public int TotalViews { get; set; }
public int TotalComments { get; set; }
}
Now suppose we have a collection of Posts like this (this is just for explanation purposes, in real life you would get a much bigger collection from database
var posts = new List<Post>
{
new Post {Id = 1, Name = "post1" , Description = "post1 description", IsPublished = true, PublishedAt = DateTime.Now, TotalViews = 50, TotalComments = 4},
new Post {Id = 2, Name = "post2" , Description = "post2 description", IsPublished = false, PublishedAt = DateTime.Now.AddDays(-3), TotalViews = 10, TotalComments = 1},
new Post {Id = 3, Name = "post3" , Description = "post3 description", IsPublished = true, PublishedAt = DateTime.Now.AddDays(-8), TotalViews = 120, TotalComments = 8},
new Post {Id = 4, Name = "post4" , Description = "post4 description", IsPublished = true, PublishedAt = DateTime.Now.AddDays(-1), TotalViews = 40, TotalComments = 5},
new Post {Id = 5, Name = "post5" , Description = "post5 description", IsPublished = true, PublishedAt = DateTime.Now, TotalViews = 0, TotalComments = 0},
new Post {Id = 6, Name = "post6" , Description = "post6 description", IsPublished = true, PublishedAt = DateTime.Now.AddDays(-10), TotalViews = 150, TotalComments = 10},
new Post {Id = 7, Name = "post7" , Description = "post7 description", IsPublished = false, PublishedAt = DateTime.Now, TotalViews = 0, TotalComments = 0},
new Post {Id = 8, Name = "post8" , Description = "post8 description", IsPublished = true, PublishedAt = DateTime.Now.AddDays(-20), TotalViews = 250, TotalComments = 15},
};
Suppose we want to allow the user to filter the collection on any property or any combination of properties, one way would be to have a function for each property and each combination of properties, something like:
public IList<Post> FilterByName(string name)
{
return posts.Where(p => p.Name == name).ToList();
}
public IList<posts> FilterByDescription(string description)
{
return posts.Where(p => p.Description.Contains(description)).ToList();
}
public IList<posts> FilterByTotalViewsGreaterOrEqual(int totalViews)
{
return posts.Where(p => p.TotalViews >= totalViews).ToList();
}
As you can see, this becomes a very tedious job since the number of functions to cover all possible combinations is quite big.
The other way to filter the collection, which is much more convenient and tidier is to build an expression tree dynamically and pass it to the where clause for filtering so Linq.Extension.PredicateBuilder comes
Installation
dotnet add package Linq.Extension.PredicateBuilder
Usage
using Linq.Extension.PredicateBuilder;
internal class Program
{
static void Main(string[] args)
{
var predicate = new PostViewComponentModel
{
Search = new List<Search>
{
new Search
{
Field = new Field{ Value = "PublishedAt" },
Operator = new Operator { Value = OperatorComparer.Between},
Value = new Value{ value = "07/08/2023" , value2 = "07/18/2023"}
},
new Search
{
Field = new Field{ Value = "Name" },
Operator = new Operator { Value = OperatorComparer.BeginsWith },
Value = new Value{ value = "p" }
},
new Search
{
Field = new Field{ Value = "Status" },
Operator = new Operator { Value = OperatorComparer.In },
Value = new Value{ value = "0,2" }
}
},
PageNumber = 1,
PageSize = 20
};
var result = new Post().GetPosts().BuildQuery(predicate.PredicateBuilderFilterRule
, new PredicateBuilderOptions() { CultureInfo = CultureInfo.CurrentCulture }).ToPaged(predicate);
}
}
public enum Status
{
New,
Pending,
Completed
}
public class PostViewComponentModel : PredicateBuilderInvokerBase<Post>
{
}
public class Post
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool IsPublished { get; set; }
public DateTime PublishedAt { get; set; }
public int TotalViews { get; set; }
public int TotalComments { get; set; }
public Status Status { get; set; }
public IQueryable<Post> GetPosts()
{
var posts = new List<Post>
{
new Post {Id = 1, Name = "POST1" , Description = "post1 description", IsPublished = true,
PublishedAt = DateTime.Now.AddMinutes(-3), TotalViews = 50, TotalComments = 4 , Status = 0},
new Post {Id = 2, Name = "POST2" , Description = "post2 description", IsPublished = false,
PublishedAt = DateTime.Now.AddDays(-1), TotalViews = 10, TotalComments = 1 , Status = (Status)1},
new Post {Id = 3, Name = "POST3" , Description = "post3 description", IsPublished = true,
PublishedAt = DateTime.Now.AddDays(-8), TotalViews = 120, TotalComments = 8, Status = (Status)2},
new Post {Id = 4, Name = "POST4" , Description = "post4 description", IsPublished = true,
PublishedAt = DateTime.Now.AddDays(-1), TotalViews = 40, TotalComments = 5, Status = (Status)1},
new Post {Id = 5, Name = "POST5" , Description = "post5 description", IsPublished = true,
PublishedAt = DateTime.Now, TotalViews = 0, TotalComments = 0 , Status = (Status)2},
new Post {Id = 6, Name = "POST6" , Description = "post6 description", IsPublished = true,
PublishedAt = DateTime.Now.AddDays(-10), TotalViews = 150, TotalComments = 10, Status = (Status)2},
new Post {Id = 7, Name = "POST7" , Description = "post7 description", IsPublished = false,
PublishedAt = DateTime.Now, TotalViews = 0, TotalComments = 0, Status = (Status)2},
new Post {Id = 8, Name = "POST8" , Description = "post8 description", IsPublished = true,
PublishedAt = DateTime.Now.AddDays(-20), TotalViews = 250, TotalComments = 15, Status = 0},
};
return posts.AsQueryable();
}
}
Top comments (0)