Specification template with entity framework using orderby and skip / take

I chose a project that uses a specification template, a template that I have not used before, and I had to go and explore the template. I noticed that it does not have the OrderBy and Skip / Take functions, and I can not find anywhere where it is shown how to implement this using the template.

I'm struggling to figure out how best to add this to the spec template. But I am having problems, such as the specification deals with " Expression<Func<T, bool>> ", while I don’t think I can store this together with orderby, etc.

Basically there is a class like this:

 public class Specification<T> : ISpecification<T> { public Expression<Func<T, bool>> Predicate { get; protected set; } public Specification(Expression<Func<T, bool>> predicate) { Predicate = predicate; } public Specification<T> And(Specification<T> specification) { return new Specification<T>(this.Predicate.And(specification.Predicate)); } public Specification<T> And(Expression<Func<T, bool>> predicate) { return new Specification<T>(this.Predicate.And(predicate)); } public Specification<T> Or(Specification<T> specification) { return new Specification<T>(this.Predicate.Or(specification.Predicate)); } public Specification<T> Or(Expression<Func<T, bool>> predicate) { return new Specification<T>(this.Predicate.Or(predicate)); } public T SatisfyingItemFrom(IQueryable<T> query) { return query.Where(Predicate).SingleOrDefault(); } public IQueryable<T> SatisfyingItemsFrom(IQueryable<T> query) { return query.Where(Predicate); } } 

This allows you to create a specification by passing a where clause. It also allows you to associate rules with "And," "Or." For instance:

 var spec = new Specification<Wave>(w => w.Id == "1").And(w => w.WaveStartSentOn > DateTime.Now); 

How to add a method for "OrderBy" and "Take"?

Since this is existing code, I cannot make any changes that could affect the existing code, and it would be nice to reorganize it. Therefore, any solution should go well with what is.

+7
source share
1 answer

What about

 public class Specification<T> : ISpecification<T> { public Expression<Func<T, bool>> Predicate { get; protected set; } public Func<IQueryable<T>, IOrderedQueryable<T>> Sort {get; protected set; } public Func<IQueryable<T>, IQueryable<T>> PostProcess {get; protected set; public Specification<T> OrderBy<TProperty>(Expression<Func<T, TProperty>> property) { var newSpecification = new Specification<T>(Predicate) { PostProcess = PostProcess } ; if(Sort != null) { newSpecification.Sort = items => Sort(items).ThenBy(property); } else { newSpecification.Sort = items => items.OrderBy(property); } return newSpecification; } public Specification<T> Take(int amount) { var newSpecification = new Specification<T>(Predicate) { Sort = Sort } ; if(PostProcess!= null) { newSpecification.PostProcess= items => PostProcess(items).Take(amount); } else { newSpecification.PostProcess= items => items.Take(amount); } return newSpecification; } public Specification<T> Skip(int amount) { var newSpecification = new Specification<T>(Predicate) { Sort = Sort } ; if(PostProcess!= null) { newSpecification.PostProcess= items => PostProcess(items).Skip(amount); } else { newSpecification.PostProcess= items => items.Skip(amount); } return newSpecification; } } 

TODO:

  • similar construction for OrderByDescending
  • Update your other methods so that the Sort value and the PostProcess value are not lost when you call AND, for example

then your Satisfaction methods:

 private IQueryable<T> Prepare(IQueryable<T> query) { var filtered = query.Where(Predicate); var sorted = Sort(filtered); var postProcessed = PostProcess(sorted); return postProcessed; } public T SatisfyingItemFrom(IQueryable<T> query) { return Prepare(query).SingleOrDefault(); } public IQueryable<T> SatisfyingItemsFrom(IQueryable<T> query) { return Prepare(query); } 

TODO: check if Sort and PostProcess are sorted in the Prepare method

Using:

 var spec = new Specification<Wave>(w => w.Id == "1") .And(w => w.WaveStartSentOn > DateTime.Now) .OrderBy(w => w.WaveStartSentOn) .Skip(20) .Take(5); 
+7
source

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


All Articles