From 7bdb84e896b062d14d228f41444eb19bd954e333 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 12 Feb 2018 04:18:44 -0600 Subject: [PATCH] added sortings. --- .../ComplexQueryTest.cs | 30 +++++++ .../PoweredSoft.DynamicLinq.Test.csproj | 1 + PoweredSoft.DynamicLinq.Test/QueryTests.cs | 31 ++++++- .../Extensions/QueryableExtensions.cs | 43 +++++++++ .../Fluent/QueryBuilder.cs | 87 +++++++++++++++++-- .../Fluent/QueryBuilderSort.cs | 18 ++++ .../Helpers/QueryableHelpers.cs | 26 ++++++ .../PoweredSoft.DynamicLinq.csproj | 1 + 8 files changed, 227 insertions(+), 10 deletions(-) create mode 100644 PoweredSoft.DynamicLinq/Fluent/QueryBuilderSort.cs diff --git a/PoweredSoft.DynamicLinq.Test/ComplexQueryTest.cs b/PoweredSoft.DynamicLinq.Test/ComplexQueryTest.cs index 1becebd..0935f2b 100644 --- a/PoweredSoft.DynamicLinq.Test/ComplexQueryTest.cs +++ b/PoweredSoft.DynamicLinq.Test/ComplexQueryTest.cs @@ -4,6 +4,7 @@ using PoweredSoft.DynamicLinq.Extensions; using PoweredSoft.DynamicLinq.Fluent; using System; using System.Collections.Generic; +using System.Data.SqlClient; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -65,5 +66,34 @@ namespace PoweredSoft.DynamicLinq.Test query = queryBuilder.Build(); Assert.AreEqual(2, query.Count()); } + + [TestMethod] + public void TestingSort() + { + // subject. + var posts = new List() + { + new Post { Id = 1, AuthorId = 1, Title = "Hello 1", Content = "World" }, + new Post { Id = 2, AuthorId = 1, Title = "Hello 2", Content = "World" }, + new Post { Id = 3, AuthorId = 2, Title = "Hello 3", Content = "World" }, + }; + + // the query. + var query = posts.AsQueryable(); + var queryBuilder = new QueryBuilder(query); + + // add some sorting. + queryBuilder + .OrderByDescending("AuthorId") + .ThenBy("Id"); + + query = queryBuilder.Build(); + + var first = query.First(); + var second = query.Skip(1).First(); + + Assert.IsTrue(first.Id == 3); + Assert.IsTrue(second.Id == 1); + } } } diff --git a/PoweredSoft.DynamicLinq.Test/PoweredSoft.DynamicLinq.Test.csproj b/PoweredSoft.DynamicLinq.Test/PoweredSoft.DynamicLinq.Test.csproj index 1e904d8..27bdbd4 100644 --- a/PoweredSoft.DynamicLinq.Test/PoweredSoft.DynamicLinq.Test.csproj +++ b/PoweredSoft.DynamicLinq.Test/PoweredSoft.DynamicLinq.Test.csproj @@ -49,6 +49,7 @@ + diff --git a/PoweredSoft.DynamicLinq.Test/QueryTests.cs b/PoweredSoft.DynamicLinq.Test/QueryTests.cs index 5a70e8f..8702b14 100644 --- a/PoweredSoft.DynamicLinq.Test/QueryTests.cs +++ b/PoweredSoft.DynamicLinq.Test/QueryTests.cs @@ -91,7 +91,7 @@ namespace PoweredSoft.DynamicLinq.Test public void LessThen() { // subject. - var authors = new List() + var posts = new List() { new Post { Id = 1, AuthorId = 1, Title = "Hello 1", Content = "World" }, new Post { Id = 2, AuthorId = 1, Title = "Hello 2", Content = "World" }, @@ -99,7 +99,7 @@ namespace PoweredSoft.DynamicLinq.Test }; // the query. - var query = authors.AsQueryable(); + var query = posts.AsQueryable(); // simple where. var newQuery = query.Where("AuthorId", ConditionOperators.LessThan, 2); @@ -112,7 +112,7 @@ namespace PoweredSoft.DynamicLinq.Test public void GreaterThanOrEqual() { // subject. - var authors = new List() + var posts = new List() { new Post { Id = 1, AuthorId = 1, Title = "Hello 1", Content = "World" }, new Post { Id = 2, AuthorId = 1, Title = "Hello 2", Content = "World" }, @@ -120,7 +120,7 @@ namespace PoweredSoft.DynamicLinq.Test }; // the query. - var query = authors.AsQueryable(); + var query = posts.AsQueryable(); // simple where. var newQuery = query.Where("AuthorId", ConditionOperators.GreaterThanOrEqual, 2); @@ -128,5 +128,28 @@ namespace PoweredSoft.DynamicLinq.Test // must match. Assert.AreEqual(1, newQuery.Count()); } + + [TestMethod] + public void TestingSort() + { + // subject. + var posts = new List() + { + new Post { Id = 1, AuthorId = 1, Title = "Hello 1", Content = "World" }, + new Post { Id = 2, AuthorId = 1, Title = "Hello 2", Content = "World" }, + new Post { Id = 3, AuthorId = 2, Title = "Hello 3", Content = "World" }, + }; + + // the query. + var query = posts.AsQueryable(); + query = query.OrderByDescending("AuthorId"); + query = query.ThenBy("Id"); + + var first = query.First(); + var second = query.Skip(1).First(); + + Assert.IsTrue(first.Id == 3); + Assert.IsTrue(second.Id == 1); + } } } diff --git a/PoweredSoft.DynamicLinq/Extensions/QueryableExtensions.cs b/PoweredSoft.DynamicLinq/Extensions/QueryableExtensions.cs index f4fa69f..00cbc4a 100644 --- a/PoweredSoft.DynamicLinq/Extensions/QueryableExtensions.cs +++ b/PoweredSoft.DynamicLinq/Extensions/QueryableExtensions.cs @@ -2,6 +2,7 @@ using PoweredSoft.DynamicLinq.Helpers; using System; using System.Collections.Generic; +using System.Data.SqlClient; using System.Linq; using System.Linq.Expressions; using System.Text; @@ -18,6 +19,8 @@ namespace PoweredSoft.DynamicLinq.Extensions return query; } + + public static IQueryable Query (this IQueryable query, Action> callback) { var queryBuilder = new QueryBuilder(query); @@ -25,5 +28,45 @@ namespace PoweredSoft.DynamicLinq.Extensions var ret = queryBuilder.Build(); return ret; } + + public static IQueryable Sort(this IQueryable query, string path, SortOrder sortOrder, bool appendSort) + { + var qb = new QueryBuilder(query); + qb.Sort(path, sortOrder, appendSort); + var ret = qb.Build(); + return ret; + } + + public static IQueryable OrderBy(this IQueryable query, string path) + { + var qb = new QueryBuilder(query); + qb.OrderBy(path); + var ret = qb.Build(); + return ret; + } + + public static IQueryable OrderByDescending(this IQueryable query, string path) + { + var qb = new QueryBuilder(query); + qb.OrderByDescending(path); + var ret = qb.Build(); + return ret; + } + + public static IQueryable ThenBy(this IQueryable query, string path) + { + var qb = new QueryBuilder(query); + qb.ThenBy(path); + var ret = qb.Build(); + return ret; + } + + public static IQueryable ThenByDescending(this IQueryable query, string path) + { + var qb = new QueryBuilder(query); + qb.ThenByDescending(path); + var ret = qb.Build(); + return ret; + } } } diff --git a/PoweredSoft.DynamicLinq/Fluent/QueryBuilder.cs b/PoweredSoft.DynamicLinq/Fluent/QueryBuilder.cs index 3fdf944..06a9bab 100644 --- a/PoweredSoft.DynamicLinq/Fluent/QueryBuilder.cs +++ b/PoweredSoft.DynamicLinq/Fluent/QueryBuilder.cs @@ -1,6 +1,7 @@ using PoweredSoft.DynamicLinq.Helpers; using System; using System.Collections.Generic; +using System.Data.SqlClient; using System.Linq; using System.Linq.Expressions; using System.Text; @@ -14,7 +15,9 @@ namespace PoweredSoft.DynamicLinq.Fluent public Type QueryableType { get; set; } - public List Parts { get; protected set; } = new List(); + public List Filters { get; protected set; } = new List(); + + public List Sorts { get; protected set; } = new List(); public QueryBuilder(IQueryable query) { @@ -25,7 +28,7 @@ namespace PoweredSoft.DynamicLinq.Fluent QueryConvertStrategy convertStrategy = QueryConvertStrategy.ConvertConstantToComparedPropertyOrField, bool and = true) { - Parts.Add(new QueryBuilderFilter + Filters.Add(new QueryBuilderFilter { And = and, ConditionOperator = conditionOperators, @@ -37,6 +40,17 @@ namespace PoweredSoft.DynamicLinq.Fluent 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. @@ -48,8 +62,8 @@ namespace PoweredSoft.DynamicLinq.Fluent // create a query part. var part = new QueryBuilderFilter(); part.And = and; - part.Parts = qb.Parts; - Parts.Add(part); + part.Parts = qb.Filters; + Filters.Add(part); //return self. return this; @@ -72,17 +86,78 @@ namespace PoweredSoft.DynamicLinq.Fluent // the query. var query = Query; - // execute the filters. + // 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; + var parameter = Expression.Parameter(typeof(T), "t"); - var expression = BuildFilterExpression(parameter, Parts); + var expression = BuildFilterExpression(parameter, Filters); var lambda = Expression.Lambda>(expression, parameter); query = query.Where(lambda); return query; diff --git a/PoweredSoft.DynamicLinq/Fluent/QueryBuilderSort.cs b/PoweredSoft.DynamicLinq/Fluent/QueryBuilderSort.cs new file mode 100644 index 0000000..e775d84 --- /dev/null +++ b/PoweredSoft.DynamicLinq/Fluent/QueryBuilderSort.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Data.SqlClient; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PoweredSoft.DynamicLinq.Fluent +{ + public class QueryBuilderSort + { + public string Path { get; set; } + + public SortOrder SortOrder { get; set; } = SortOrder.Ascending; + + public bool AppendSort { get; set; } + } +} diff --git a/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs b/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs index d8f988f..129eb51 100644 --- a/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs +++ b/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Data.SqlClient; using System.Linq; using System.Linq.Expressions; using System.Text; @@ -91,5 +92,30 @@ namespace PoweredSoft.DynamicLinq.Helpers throw new NotSupportedException($"{convertStrategy} supplied is not recognized"); } + + public static IQueryable CreateSortExpression(IQueryable query, string sortPath, SortOrder sortOrder, bool appendSort = true) + { + var parameter = Expression.Parameter(typeof(T), "t"); + var member = QueryableHelpers.ResolvePathForExpression(parameter, sortPath); + + string sortCommand = null; + if (sortOrder == SortOrder.Descending) + sortCommand = appendSort == false ? "OrderByDescending" : "ThenByDescending"; + else + sortCommand = appendSort == false ? "OrderBy" : "ThenBy"; + + var expression = Expression.Lambda(member, parameter); + + var resultExpression = Expression.Call + (typeof(Queryable), + sortCommand, + new Type[] { typeof(T), member.Type }, + query.Expression, + Expression.Quote(expression) + ); + + query = query.Provider.CreateQuery(resultExpression); + return query; + } } } diff --git a/PoweredSoft.DynamicLinq/PoweredSoft.DynamicLinq.csproj b/PoweredSoft.DynamicLinq/PoweredSoft.DynamicLinq.csproj index 78214a0..d8ad431 100644 --- a/PoweredSoft.DynamicLinq/PoweredSoft.DynamicLinq.csproj +++ b/PoweredSoft.DynamicLinq/PoweredSoft.DynamicLinq.csproj @@ -42,6 +42,7 @@ +