using PoweredSoft.DynamicLinq.Helpers; using System; using System.Collections.Generic; using System.Data.SqlClient; using System.Linq; using System.Linq.Expressions; using System.Text; using System.Threading.Tasks; namespace PoweredSoft.DynamicLinq.Fluent { public class QueryBuilder { public IQueryable Query { get; set; } public Type QueryableType { get; set; } public bool IsNullCheckingEnabled { get; protected set; } = false; public List Filters { get; protected set; } = new List(); public List Sorts { get; protected set; } = new List(); public QueryBuilder(IQueryable query) { Query = query; } public QueryBuilder NullChecking(bool check = true) { IsNullCheckingEnabled = check; return this; } public virtual QueryBuilder Compare(string path, ConditionOperators conditionOperators, object value, QueryConvertStrategy convertStrategy = QueryConvertStrategy.ConvertConstantToComparedPropertyOrField, bool and = true, QueryCollectionHandling collectionHandling = QueryCollectionHandling.Any) { Filters.Add(new QueryBuilderFilter { And = and, ConditionOperator = conditionOperators, Path = path, Value = value, ConvertStrategy = convertStrategy, CollectionHandling = collectionHandling }); return this; } public virtual QueryBuilder Sort(string path, SortOrder sortOrder, bool appendSort) { Sorts.Add(new QueryBuilderSort { Path = path, SortOrder = sortOrder, AppendSort = appendSort }); return this; } public virtual QueryBuilder SubQuery(Action> subQuery, bool and = true) { // create query builder for same type. var qb = new QueryBuilder(Query); qb.NullChecking(IsNullCheckingEnabled); // callback. subQuery(qb); // create a query part. var part = new QueryBuilderFilter(); part.And = and; part.Filters = qb.Filters; Filters.Add(part); //return self. return this; } public QueryBuilder And(string path, ConditionOperators conditionOperator, object value, QueryConvertStrategy convertStrategy = QueryConvertStrategy.ConvertConstantToComparedPropertyOrField, QueryCollectionHandling collectionHandling = QueryCollectionHandling.Any) => Compare(path, conditionOperator, value, convertStrategy: convertStrategy, collectionHandling: collectionHandling, and: true); public QueryBuilder Or(string path, ConditionOperators conditionOperator, object value, QueryConvertStrategy convertStrategy = QueryConvertStrategy.ConvertConstantToComparedPropertyOrField, QueryCollectionHandling collectionHandling = QueryCollectionHandling.Any) => Compare(path, conditionOperator, value, convertStrategy: convertStrategy, collectionHandling: collectionHandling, and: false); public QueryBuilder And(Action> subQuery) => SubQuery(subQuery, true); public QueryBuilder Or(Action> subQuery) => SubQuery(subQuery, false); public virtual IQueryable Build() { // the query. var query = Query; // build the filters. query = BuildFilters(query); // build the sorts query = BuildSorts(query); return query; } public virtual QueryBuilder OrderBy(string path) { Sorts.Clear(); Sorts.Add(new QueryBuilderSort { Path = path, SortOrder = SortOrder.Ascending, AppendSort = false }); return this; } public virtual QueryBuilder OrderByDescending(string path) { Sorts.Clear(); Sorts.Add(new QueryBuilderSort { Path = path, SortOrder = SortOrder.Descending, AppendSort = false }); return this; } public virtual QueryBuilder ThenBy(string path) { Sorts.Add(new QueryBuilderSort { Path = path, SortOrder = SortOrder.Ascending, AppendSort = true }); return this; } public virtual QueryBuilder ThenByDescending(string path) { Sorts.Add(new QueryBuilderSort { Path = path, SortOrder = SortOrder.Descending, AppendSort = true }); return this; } protected virtual IQueryable BuildSorts(IQueryable query) { Sorts.ForEach(sort => { query = QueryableHelpers.CreateSortExpression(query, sort.Path, sort.SortOrder, sort.AppendSort); }); return query; } protected virtual IQueryable BuildFilters(IQueryable query) { if (Filters == null || Filters?.Count() == 0) return query; // shared parameter. var sharedParameter = Expression.Parameter(typeof(T), "t"); // build the expression. var filterExpressionMerged = BuildFilterExpression(sharedParameter, Filters); // make changes on the query. query = query.Where(filterExpressionMerged); return query; } protected virtual Expression> BuildFilterExpression(ParameterExpression parameter, List filters) { Expression> temp = null; filters.ForEach(filter => { Expression> innerExpression; if (filter.Filters?.Any() == true) innerExpression = BuildFilterExpression(parameter, filter.Filters); else innerExpression = BuildFilterExpression(parameter, filter); if (temp == null) { temp = innerExpression; } else { if (filter.And) temp = Expression.Lambda>(Expression.AndAlso(temp.Body, innerExpression.Body), parameter); else temp = Expression.Lambda>(Expression.OrElse(temp.Body, innerExpression.Body), parameter); } }); return temp; } protected virtual Expression> BuildFilterExpression(ParameterExpression parameter, QueryBuilderFilter filter) { var ret = QueryableHelpers.CreateFilterExpression( filter.Path, filter.ConditionOperator, filter.Value, filter.ConvertStrategy, filter.CollectionHandling, parameter: parameter, nullChecking: IsNullCheckingEnabled ); return ret; } } }