Strongly typed Linq filtering method

I'm a little tired of writing service layer lines, such as:

The following are only examples for readers. Therefore, they may have errors or typos, sorry for that :)

//ViewModel public class EntityIndexFilterPartial { public int? Id { get; set; } public DateTime? StartDate { get; set; } public DateTime? EndDate { get; set; } public IEnumerable<SelectListItem> StatusList { get; set; } public int? StatusID { get; set; } } //Service Layer method //Method parameters are reperesents view model properties public IQueryable<Entity> FilterBy(int? id, DateTime? startDate, DateTime? endDate, int? statusId) { var filter = _db.Entities.AsQueryable(); if (id.HasValue) filter = filter.Where(x => x.Id == id.Value); if (startDate.HasValue) filter = filter.Where(x => x.StartDate >= startDate.Value); if (endDate.HasValue) filter = filter.Where(x => x.EndDate <= endDate.Value); if (statusId.HasValue) filter = filter.Where(x => x.EntityStatus.StatusID == statusId); return filter; } 

I am looking for enough for some smart codes. I know about the LINQ dynamic library , and I use it too. But I'm looking for strongly typed filtering . I do not want to write magic lines or some of them.

So basically I found some solutions, but I want to hear some well-written smart codes from this community. Of course, there can be dozens of solutions, but then again, how would you write your strictly typed filtering service level codes. Any ideas ...

Here are some of my solutions:

Solution 1: The same FilterBy method, but the parameters are different, now a list of expressions. Thus, this means that I create a predicate List in the controller and post it here.

 public IQueryable<Entity> FilterBy(List<Expression<Func<Entity,bool>>> predicateList) { var filter = _db.Entities.AsQueryable(); foreach (var item in predicateList) { filter = filter.FilterBy(item); } return filter; } 

Solution 2: FilterBy using the EntityIndexFilterPartial parameter as a parameter at the application service level (and not for the domain service). I am sure that there are some problems in this design, but I want to hear your opinions.

 public IQueryable<Entity> FilterBy(EntityIndexFilterPartial filterPartial) { //I'm calling the domain service layer FilterBy method such as like in solution 1. } 

Solution 3: I think this is much better than others, but I still think of something simpler and better codes.

 //helper class public static class FilterByHelper { public static IQueryable<T> If<T>(this IQueryable<T> filter, bool condition, Expression<Func<T, bool>> predicate) { if (condition) return filter.FilterBy(predicate); return filter; } public static IQueryable<T> FilterBy<T>(this IQueryable<T> filter, Expression<Func<T, bool>> predicate) { return filter.Where(predicate); } } public IQueryable<Entity> FilterBy(int? id, DateTime? startDate, DateTime? endDate, int? statusId) { return _db.Entities .If(id.HasValue, x => x.Id == id.Value) .If(startDate.HasValue, x => x.StartDate >= startDate.Value) .If(endDate.HasValue, x => x.EndDate <= endDate.Value) .If(statusId.HasValue, x => x.EntityStatus.StatusID == statusId); } 

I know this has become a bit of a long question, but I hope I clearly ask what I want to ask.

As a quick and easy question, do you know any reasonably designed code to save us from writing the same lines of these filter codes?

Btw, I am not looking for template solutions or huge answers, you can give me an example or say how to find the best way enough.

Of course, if you write the full answer explained, I will be appricated.

Thanks.

+6
source share
3 answers

Have you tried simple conditions || ?

 return _db.Entities .Where(x => id == null || x.Id == id.Value) .Where(x => startDate == null || x.StartDate >= startDate.Value) .Where(x => endDate == null || x.EndDate <= endDate.Value) .Where(x => statusId == null || x => x.EntityStatus.StatusID == statusId); 

? I would expect that after optimizing the query, no-op filters would be equivalent to not adding filters at all.

+8
source

I solve this problem using quick extension methods. I think this is a pretty nice solution for this class of problems. The best part about using the free style for defining your filters is that it creates really comprehensible code when you actually consume it. In practice, this makes your service level more dynamic.

eg

If you will have

 public class User { public int Id {get;set;} public DateTime CreatedOn {get;set;} public string Name {get;set;} public DateTime BirthDate {get;set;} } 

you can write some filters similar to this

 public IQueriable<User> OlderThan(this IQueriable<User> users, DateTime olderThan){/*implementation*/} public IQueriable<User> CreatedAfter(this IQueriable<User> users, DateTime createdAfter){/*implementation*/} //or something more generic public IQueriable<User> WhereName(this IQueriable<User> users, Expression<Func<string,bool>> nameQuery){/*implementation*/} 

and then move these things like this:

 users .CreatedAfter(new DateTime(2011,1,1)) .OlderThan(new DateTime(1985,1,2)) .WhereName(n=>n.StartsWith("L")); 

this allows your filtering logic to be a bit more dynamic without creating style filters that are difficult to maintain and complex

In practice, this means

  • Filters can be very simple.
  • you can create aggregation filters that use a different filter inside
  • Filters perform only one purpose
  • they may have business names

In my blog, I talk about this method with some real world examples. http://blog.staticvoid.co.nz/2013/2/25/a_case_for_generic_repositories

+2
source

Hi Yusuf This post may be useful for you. Solution looks smart, it implements dynamic conditions where conditions for linq query look simple.

http://amitech.co/amitech-lab/item/dynamically-add-conditions-in-linq

Hope this helps

+2
source

Source: https://habr.com/ru/post/950935/


All Articles