A “specification” in programming is a design pattern by which the representation of business logic rules can be transformed into a chain of objects connected by boolean logic operations.
public interface ISpecification { bool IsSatisfiedBy(object candidate); } Expression<Func<T, bool>> and Func<T, bool>> , the signature of which coincides with IsSatisfiedBy public class UserQueryExtensions { public static IQueryable<User> WhereGroupNameIs(this IQueryable<User> users, string name) { return users.Where(u => u.GroupName == name); } }  public abstract class Specification<T> { public bool IsSatisfiedBy(T item) { return SatisfyingElementsFrom(new[] { item }.AsQueryable()).Any(); } public abstract IQueryable<T> SatisfyingElementsFrom(IQueryable<T> candidates); }  public interface IQueryableSpecification<T> where T: class { IQueryable<T> Apply(IQueryable<T> query); } public interface IQueryableOrderBy<T> { IOrderedQueryable<T> Apply(IQueryable<T> queryable); } public static bool Satisfy<T>(this T obj, Func<T, bool> spec) => spec(obj); public static bool SatisfyExpresion<T>(this T obj, Expression<Func<T, bool>> spec) => spec.AsFunc()(obj); public static bool IsSatisfiedBy<T>(this Func<T, bool> spec, T obj) => spec(obj); public static bool IsSatisfiedBy<T>(this Expression<Func<T, bool>> spec, T obj) => spec.AsFunc()(obj); public static IQueryable<T> Where<T>(this IQueryable<T> source, IQueryableSpecification<T> spec) where T : class => spec.Apply(source); Func<T, bool> ?Func very difficult to go to Expression . Often you need to transfer the filtering to the level of building a query to the database, otherwise you will have to pull out millions of records and filter them in memory, which is not optimal.Expression<Func<T, bool>> ?Expression to Func , on the contrary, is trivial: var func = expression.Compile() . However, the layout of the Expression is not a trivial task . It is no longer pleasant if a conditional assembly of the expression is required (for example, if the specification contains three parameters, two of which are optional). And really bad Expression<Func<T, bool>> does it in cases requiring subqueries like query.Where(x => someOtherQuery.Contains(x.Id)) .IQueryable and transmit further through the fluent interface. Additional Where methods allow code to appear as if it were a normal LINQ transformation chain. public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, IQueryableOrderBy<T> spec) where T : class => spec.Apply(source); public interface IQueryableOrderBy<T> { IOrderedQueryable<T> Apply(IQueryable<T> queryable); } Reflection , you can write a basic object to filter anything in a declarative style. The code below analyzes the public properties of an AutoSpec and the type to which you want to apply filtering. If a match is found and the AutoSpec inheritor AutoSpec filled in, a filtering rule by this field will be automatically added to the Queryable . public class AutoSpec<TProjection> : IPaging, ILinqSpecification<TProjection>, ILinqOrderBy<TProjection> where TProjection : class, IHasId { public virtual IQueryable<TProjection> Apply(IQueryable<TProjection> query) => GetType() .GetPublicProperties() .Where(x => typeof(TProjection).GetPublicProperties().Any(y => x.Name == y.Name)) .Aggregate(query, (current, next) => { var val = next.GetValue(this); if (val == null) return current; return current.Where(next.PropertyType == typeof(string) ? $"{next.Name}.StartsWith(@0)" : $"{next.Name}=@0", val); }); IOrderedQueryable<TProjection> ILinqOrderBy<TProjection>.Apply(IQueryable<TProjection> queryable) => !string.IsNullOrEmpty(OrderBy) ? queryable.OrderBy(OrderBy) : queryable.OrderBy(x => x.Id); } AutoSpeccan be implemented withoutDynamic Linq, using onlyExpression, but the implementation does not fit into ten lines and the code will be much more difficult to understand.
IQueryableSpec does not meet the layout requirements. The fact is that I rarely have to deal with the need to do ||, and && is achieved by a simple query.Where(spec1).Where(spec2) . I decided to do a little refactoring to make the code cleaner:  //  IQueryableSpecification  IQueryableFilter public interface IQueryableFilter<T> where T: class { IQueryable<T> Apply(IQueryable<T> query); } Expression<Func<T, bool>> enough for me  /// <summary> /// Creates a predicate that evaluates to true. /// </summary> public static Expression<Func<T, bool>> True<T>() { return param => true; } /// <summary> /// Creates a predicate that evaluates to false. /// </summary> public static Expression<Func<T, bool>> False<T>() { return param => false; } /// <summary> /// Creates a predicate expression from the specified lambda expression. /// </summary> public static Expression<Func<T, bool>> Create<T>(Expression<Func<T, bool>> predicate) { return predicate; } /// <summary> /// Combines the first predicate with the second using the logical "and". /// </summary> public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) { return first.Compose(second, Expression.AndAlso); } /// <summary> /// Combines the first predicate with the second using the logical "or". /// </summary> public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) { return first.Compose(second, Expression.OrElse); } Compose method are explained in the link above. Now add syntactic sugar so that you can use && and || and IHasId restriction on generic, because I am not interested in creating specifications for the Value Object . This restriction is not necessary, it just seems better to me.  public static class SpecificationExtenions { public static Specification<T> AsSpec<T>(this Expression<Func<T, bool>> expr) where T : class, IHasId => new Specification<T>(expr); } public sealed class Specification<T> : IQueryableFilter<T> where T: class, IHasId { public Expression<Func<T, bool>> Expression { get; } public Specification(Expression<Func<T, bool>> expression) { Expression = expression; if (expression == null) throw new ArgumentNullException(nameof(expression)); } public static implicit operator Expression<Func<T, bool>>(Specification<T> spec) => spec.Expression; public static bool operator false(Specification<T> spec) { return false; } public static bool operator true(Specification<T> spec) { return false; } public static Specification<T> operator &(Specification<T> spec1, Specification<T> spec2) => new Specification<T>(spec1.Expression.And(spec2.Expression)); public static Specification<T> operator |(Specification<T> spec1, Specification<T> spec2) => new Specification<T>(spec1.Expression.Or(spec2.Expression)); public static Specification<T> operator !(Specification<T> spec) => new Specification<T>(spec.Expression.Not()); public IQueryable<T> Apply(IQueryable<T> query) => query.Where(Expression); public bool IsSatisfiedBy(T obj) => Expression.AsFunc()(obj); }   public class Category : HasIdBase<int> { public static readonly Expression<Func<Category, bool>> NiceRating = x => x.Rating > 50; public static readonly Expression<Func<Category, bool>> BadRating = x => x.Rating < 10; public static readonly Expression<Func<Category, bool>> Active= x => x.IsDeleted == false; //... } var niceCategories = db.Query<Category>.Where(Category.NiceRating);   public class Category : HasIdBase<int> { public static readonly Specification<Category> NiceRating = new Specification(x => x.Rating > 50); //... } var niceCategories = db.Query<Category> .Where((Category.NiceRating || Category.BadRating) && Category.IsActive); DynamicLinq . We'll have to work a bit with expression trees. public enum Compose { And, Or } public static Spec<T> AsSpec<T>(this object obj, Compose compose = Compose.And) where T : class, IHasId { var filterProps = obj.GetType() .GetPublicProperties() .ToArray(); var filterPropNames = filterProps .Select(x => x.Name) .ToArray(); var props = typeof(T) .GetPublicProperties() .Where(x => filterPropNames.Contains(x.Name)) .Select(x => new { Property = x, Value = filterProps.Single(y => y.Name == x.Name).GetValue(obj) }) .Where(x => x.Value != null) .Select(x => { //     e => e.Prop == Val var parameter = Expression.Parameter(typeof (T)); var property = Expression.Property(parameter, x.Property); var body = Expression.Equal(property, Expression.Constant(x.Value)); var delegateType = typeof(Func<T, bool>); return (Expression<Func<T, bool>>) Expression.Lambda(delegateType, body, parameter); }) .ToArray(); if (!props.Any()) return new Spec<T>(x => true); //    ||  && var expr = compose == Compose.And ? props.Aggregate((c, n) => c.And(n)) : props.Aggregate((c, n) => c.Or(n)); return expr.AsSpec(); } Source: https://habr.com/ru/post/325280/
All Articles