From 0415364fea3d0503a441238cb21d671768b72792 Mon Sep 17 00:00:00 2001 From: Hadi Khazaee Asl Date: Tue, 4 Nov 2025 18:10:53 +0330 Subject: [PATCH] add some Extensions Methods for XQuery Pages Counting ... --- Extensions/IQueryableExtensions.cs | 380 ++++++++++++++++++----------- 1 file changed, 244 insertions(+), 136 deletions(-) diff --git a/Extensions/IQueryableExtensions.cs b/Extensions/IQueryableExtensions.cs index b03fc34..3fe886d 100644 --- a/Extensions/IQueryableExtensions.cs +++ b/Extensions/IQueryableExtensions.cs @@ -6,12 +6,16 @@ using System.Reflection; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using xCommons.Extensions; +using xDataService.Configuration; using xDataService.Constants; using xExceptions.Constants; using xModels.Base; +using xModels.Dtos; -namespace xDataService.Extensions { - public static class IQueryableExtensions { +namespace xDataService.Extensions +{ + public static class IQueryableExtensions + { /// /// Apply Search Filter on a Query /// @@ -19,14 +23,15 @@ namespace xDataService.Extensions { /// /// /// - public static IQueryable ApplyFilter ( + public static IQueryable ApplyFilter( this IQueryable source, string filter ) - where T : XBaseEntity { + where T : XBaseEntity + { // // Apply Filter ... - return source.Where (r => r.PropValuesContains (filter)); + return source.Where(r => r.PropValuesContains(filter)); } /// @@ -36,38 +41,41 @@ namespace xDataService.Extensions { /// /// /// - public static async Task> ApplyFilterAsync ( + public static async Task> ApplyFilterAsync( this IQueryable source, string filter ) - where T : XBaseEntity { + where T : XBaseEntity + { // // Where Clause ... Expression> whereClause = r => r - .PropValuesContains (filter); + .PropValuesContains(filter); // // Generate Where Function ... - var whereFunc = whereClause.Compile (); + var whereFunc = whereClause.Compile(); // // Get Enumerable ... var enumerator = source - .AsAsyncEnumerable (); + .AsAsyncEnumerable(); // - var result = new List (); + var result = new List(); await - foreach (var entity in enumerator) { + foreach (var entity in enumerator) + { // - var isApproved = whereFunc (entity); - if (isApproved) { - result.Add (entity); + var isApproved = whereFunc(entity); + if (isApproved) + { + result.Add(entity); } } // - return result.AsQueryable (); + return result.AsQueryable(); } /// @@ -77,40 +85,43 @@ namespace xDataService.Extensions { /// /// /// - public static async Task> ApplyFilterAsync ( + public static async Task> ApplyFilterAsync( this IEnumerable source, string filter ) - where T : XBaseEntity { + where T : XBaseEntity + { // // Where Clause ... Expression> whereClause = r => r - .PropValuesContains (filter); + .PropValuesContains(filter); // // Generate Where Function ... - var whereFunc = whereClause.Compile (); + var whereFunc = whereClause.Compile(); // // Get Enumerable ... var enumerator = source - .AsQueryable () - .AsAsyncEnumerable (); + .AsQueryable() + .AsAsyncEnumerable(); // - var result = new List (); + var result = new List(); await - foreach (var entity in enumerator) { + foreach (var entity in enumerator) + { // - var isApproved = whereFunc (entity); - if (isApproved) { - result.Add (entity); + var isApproved = whereFunc(entity); + if (isApproved) + { + result.Add(entity); } } // return result - .AsEnumerable (); + .AsEnumerable(); } /// @@ -120,14 +131,15 @@ namespace xDataService.Extensions { /// /// /// - public static IEnumerable ApplyFilter ( + public static IEnumerable ApplyFilter( this IEnumerable source, string filter ) - where T : class { + where T : class + { // // Apply Filter ... - return source.Where (r => r.PropValuesContains (filter)); + return source.Where(r => r.PropValuesContains(filter)); } /// @@ -137,43 +149,46 @@ namespace xDataService.Extensions { /// /// /// - public static IOrderedQueryable OrderBy ( + public static IOrderedQueryable OrderBy( this IQueryable query, string propertyName - ) { + ) + { // - var entityType = typeof (TSource); + var entityType = typeof(TSource); // // Create x => x.PropName - var propertyInfo = entityType.GetProperty (propertyName); - if (propertyInfo.IsNull ()) { - XException.InvalidArgs.Throw (); + var propertyInfo = entityType.GetProperty(propertyName); + if (propertyInfo.IsNull()) + { + XException.InvalidArgs.Throw(); } // - ParameterExpression arg = Expression.Parameter (entityType, "x"); - MemberExpression property = Expression.Property (arg, propertyName); - var selector = Expression.Lambda (property, new ParameterExpression[] { arg }); + ParameterExpression arg = Expression.Parameter(entityType, "x"); + MemberExpression property = Expression.Property(arg, propertyName); + var selector = Expression.Lambda(property, new ParameterExpression[] { arg }); // // Get System.Linq.Queryable.OrderBy() method. - var enumarableType = typeof (System.Linq.Queryable); - var method = enumarableType.GetMethods () - .Where (m => m.Name == "OrderBy" && m.IsGenericMethodDefinition) - .Where (m => { + var enumarableType = typeof(System.Linq.Queryable); + var method = enumarableType.GetMethods() + .Where(m => m.Name == "OrderBy" && m.IsGenericMethodDefinition) + .Where(m => + { // - var parameters = m.GetParameters ().ToList (); + var parameters = m.GetParameters().ToList(); // // Put more restriction here to ensure selecting the right overload return parameters.Count == 2; // overload that has 2 parameters - }).Single (); + }).Single(); // // The linq's OrderBy has two generic types, which provided here ... MethodInfo genericMethod = method - .MakeGenericMethod (entityType, propertyInfo.PropertyType); + .MakeGenericMethod(entityType, propertyInfo.PropertyType); /* Call query.OrderBy(selector), with query and selector: x=> x.PropName @@ -181,8 +196,8 @@ namespace xDataService.Extensions { By doing so EF can extract "order by" columns and generate SQL for it. */ - var newQuery = (IOrderedQueryable) genericMethod - .Invoke (genericMethod, new object[] { query, selector }); + var newQuery = (IOrderedQueryable)genericMethod + .Invoke(genericMethod, new object[] { query, selector }); // return newQuery; @@ -195,19 +210,21 @@ namespace xDataService.Extensions { /// /// /// - public static IOrderedEnumerable OrderBy ( + public static IOrderedEnumerable OrderBy( this IEnumerable query, string propertyName - ) { + ) + { // - var entityType = typeof (TSource); - var propertyInfo = entityType.GetProperty (propertyName); - if (propertyInfo.IsNull ()) { - XException.InvalidArgs.Throw (); + var entityType = typeof(TSource); + var propertyInfo = entityType.GetProperty(propertyName); + if (propertyInfo.IsNull()) + { + XException.InvalidArgs.Throw(); } // - return query.OrderBy (x => propertyInfo.GetValue (x, null)); + return query.OrderBy(x => propertyInfo.GetValue(x, null)); } /// @@ -217,43 +234,46 @@ namespace xDataService.Extensions { /// /// /// - public static IOrderedQueryable OrderByDescending ( + public static IOrderedQueryable OrderByDescending( this IQueryable query, string propertyName - ) { + ) + { // - var entityType = typeof (TSource); + var entityType = typeof(TSource); // // Create x => x.PropName - var propertyInfo = entityType.GetProperty (propertyName); - if (propertyInfo.IsNull ()) { - XException.InvalidArgs.Throw (); + var propertyInfo = entityType.GetProperty(propertyName); + if (propertyInfo.IsNull()) + { + XException.InvalidArgs.Throw(); } // - ParameterExpression arg = Expression.Parameter (entityType, "x"); - MemberExpression property = Expression.Property (arg, propertyName); - var selector = Expression.Lambda (property, new ParameterExpression[] { arg }); + ParameterExpression arg = Expression.Parameter(entityType, "x"); + MemberExpression property = Expression.Property(arg, propertyName); + var selector = Expression.Lambda(property, new ParameterExpression[] { arg }); // // Get System.Linq.Queryable.OrderBy() method. - var enumarableType = typeof (System.Linq.Queryable); - var method = enumarableType.GetMethods () - .Where (m => m.Name == "OrderByDescending" && m.IsGenericMethodDefinition) - .Where (m => { + var enumarableType = typeof(System.Linq.Queryable); + var method = enumarableType.GetMethods() + .Where(m => m.Name == "OrderByDescending" && m.IsGenericMethodDefinition) + .Where(m => + { // - var parameters = m.GetParameters ().ToList (); + var parameters = m.GetParameters().ToList(); // // Put more restriction here to ensure selecting the right overload return parameters.Count == 2; // overload that has 2 parameters - }).Single (); + }).Single(); // // The linq's OrderBy has two generic types, which provided here ... MethodInfo genericMethod = method - .MakeGenericMethod (entityType, propertyInfo.PropertyType); + .MakeGenericMethod(entityType, propertyInfo.PropertyType); /* Call query.OrderBy(selector), with query and selector: x=> x.PropName @@ -261,8 +281,8 @@ namespace xDataService.Extensions { By doing so EF can extract "order by" columns and generate SQL for it. */ - var newQuery = (IOrderedQueryable) genericMethod - .Invoke (genericMethod, new object[] { query, selector }); + var newQuery = (IOrderedQueryable)genericMethod + .Invoke(genericMethod, new object[] { query, selector }); // return newQuery; @@ -275,19 +295,21 @@ namespace xDataService.Extensions { /// /// /// - public static IOrderedEnumerable OrderByDescending ( + public static IOrderedEnumerable OrderByDescending( this IEnumerable query, string propertyName - ) { + ) + { // - var entityType = typeof (TSource); - var propertyInfo = entityType.GetProperty (propertyName); - if (propertyInfo.IsNull ()) { - XException.InvalidArgs.Throw (); + var entityType = typeof(TSource); + var propertyInfo = entityType.GetProperty(propertyName); + if (propertyInfo.IsNull()) + { + XException.InvalidArgs.Throw(); } // - return query.OrderByDescending (x => propertyInfo.GetValue (x, null)); + return query.OrderByDescending(x => propertyInfo.GetValue(x, null)); } /// @@ -299,21 +321,24 @@ namespace xDataService.Extensions { /// /// /// - public static IQueryable ApplySorting ( + public static IQueryable ApplySorting( this IQueryable source, string sortBy, bool isAscending, IDictionary>> columnsMap = null ) - where T : XBaseEntity { + where T : XBaseEntity + { // - if (columnsMap == null) { + if (columnsMap == null) + { // - columnsMap = source.FirstOrDefault () - .GetDefaultColumnsMap (); + columnsMap = source.FirstOrDefault() + .GetDefaultColumnsMap(); // - if (columnsMap == null) { + if (columnsMap == null) + { return source; } } @@ -321,18 +346,22 @@ namespace xDataService.Extensions { // // Validate SortBy ... var sortByItem = columnsMap.Keys - .FirstOrDefault (p => - p.ToNormalString () == sortBy.ToNormalString ()); - if (sortByItem.IsNullOrEmpty ()) { + .FirstOrDefault(p => + p.ToNormalString() == sortBy.ToNormalString()); + if (sortByItem.IsNullOrEmpty()) + { return source; } // // Apply Sorting ... - if (isAscending) { - return source.OrderBy (sortByItem); - } else { - return source.OrderByDescending (sortByItem); + if (isAscending) + { + return source.OrderBy(sortByItem); + } + else + { + return source.OrderByDescending(sortByItem); } } @@ -345,21 +374,24 @@ namespace xDataService.Extensions { /// /// /// - public static IEnumerable ApplySorting ( + public static IEnumerable ApplySorting( this IEnumerable source, string sortBy, bool isAscending, IDictionary>> columnsMap = null ) - where T : XBaseEntity { + where T : XBaseEntity + { // - if (columnsMap == null) { + if (columnsMap == null) + { // - columnsMap = source.FirstOrDefault () - .GetDefaultColumnsMap (); + columnsMap = source.FirstOrDefault() + .GetDefaultColumnsMap(); // - if (columnsMap == null) { + if (columnsMap == null) + { return source; } } @@ -367,18 +399,22 @@ namespace xDataService.Extensions { // // Validate SortBy ... var sortByItem = columnsMap.Keys - .FirstOrDefault (p => - p.ToNormalString () == sortBy.ToNormalString ()); - if (sortByItem.IsNullOrEmpty ()) { + .FirstOrDefault(p => + p.ToNormalString() == sortBy.ToNormalString()); + if (sortByItem.IsNullOrEmpty()) + { return source; } // // Apply Sorting ... - if (isAscending) { - return source.OrderBy (sortByItem); - } else { - return source.OrderByDescending (sortByItem); + if (isAscending) + { + return source.OrderBy(sortByItem); + } + else + { + return source.OrderByDescending(sortByItem); } } @@ -391,21 +427,24 @@ namespace xDataService.Extensions { /// /// /// - public static IEnumerable ApplySorting ( + public static IEnumerable ApplySorting( this IEnumerable source, string sortBy, bool isAscending, IDictionary>> columnsMap = null ) - where T : class { + where T : class + { // - if (columnsMap == null) { + if (columnsMap == null) + { // - columnsMap = source.FirstOrDefault () - .GetDefaultColumnsMap (); + columnsMap = source.FirstOrDefault() + .GetDefaultColumnsMap(); // - if (columnsMap == null) { + if (columnsMap == null) + { return source; } } @@ -413,18 +452,22 @@ namespace xDataService.Extensions { // // Validate SortBy ... var sortByItem = columnsMap.Keys - .FirstOrDefault (p => - p.ToNormalString () == sortBy.ToNormalString ()); - if (sortByItem.IsNullOrEmpty ()) { + .FirstOrDefault(p => + p.ToNormalString() == sortBy.ToNormalString()); + if (sortByItem.IsNullOrEmpty()) + { return source; } // // Apply Sorting ... - if (isAscending) { - return source.OrderBy (sortByItem); - } else { - return source.OrderByDescending (sortByItem); + if (isAscending) + { + return source.OrderBy(sortByItem); + } + else + { + return source.OrderByDescending(sortByItem); } } @@ -436,32 +479,35 @@ namespace xDataService.Extensions { /// /// /// - public static IQueryable ApplyPaging ( + public static IQueryable ApplyPaging( this IQueryable source, int? page, int? pageSize - ) { + ) + { // if (!page.HasValue || - page <= 0) { + page <= 0) + { page = 1; } // if (!pageSize.HasValue || pageSize <= 0 || - pageSize > XDataServiceConstants.MAX_AVAILABLE_PAGE_SIZE) { + pageSize > XDataServiceConstants.MAX_AVAILABLE_PAGE_SIZE) + { pageSize = XDataServiceConstants.DEFAULT_PAGE_SIZE; } // - var pageVal = page.GetValueOrDefault (); - var pageSizeVal = pageSize.GetValueOrDefault (); + var pageVal = page.GetValueOrDefault(); + var pageSizeVal = pageSize.GetValueOrDefault(); // return source - .Skip ((pageVal - 1) * pageSizeVal) - .Take (pageSizeVal); + .Skip((pageVal - 1) * pageSizeVal) + .Take(pageSizeVal); } /// @@ -472,32 +518,94 @@ namespace xDataService.Extensions { /// /// /// - public static IEnumerable ApplyPaging ( + public static IEnumerable ApplyPaging( this IEnumerable source, int? page, int? pageSize - ) { + ) + { // if (!page.HasValue || - page <= 0) { + page <= 0) + { page = 1; } // if (!pageSize.HasValue || pageSize <= 0 || - pageSize > XDataServiceConstants.MAX_AVAILABLE_PAGE_SIZE) { + pageSize > XDataServiceConstants.MAX_AVAILABLE_PAGE_SIZE) + { pageSize = XDataServiceConstants.DEFAULT_PAGE_SIZE; } // - var pageVal = page.GetValueOrDefault (); - var pageSizeVal = pageSize.GetValueOrDefault (); + var pageVal = page.GetValueOrDefault(); + var pageSizeVal = pageSize.GetValueOrDefault(); // return source - .Skip ((pageVal - 1) * pageSizeVal) - .Take (pageSizeVal); + .Skip((pageVal - 1) * pageSizeVal) + .Take(pageSizeVal); + } + + /// + /// Count Query Pages based on Specified Total Number ... + /// + /// + /// + /// + public static int CountPages(this XQuery source, int totalItems) + { + // + int result = 0; + + // + // Validate and Calculate ... + if (!source.IsNull() && source.PageSize > 0 && totalItems > 0) + { + // + result = totalItems / source.PageSize; + if (totalItems % source.PageSize > 0) + { + result++; + } + } + + // + return result; + } + + /// + /// Normalize an XQuery based on Data Service Configurations ... + /// + /// + /// + /// + public static XQuery NormalizeQuery( + this XQuery source, + XDataServiceConfiguration configuration // + ) + { + // + if (source.IsNull()) + { + source = new XQuery(); + } + + // + // Page Size Normalization ... + if (source.PageSize < configuration.PagingConfiguration.MinAvailablePageSize) + { + source.PageSize = configuration.PagingConfiguration.MinAvailablePageSize; + } + if (source.PageSize > configuration.PagingConfiguration.MaxAvailablePageSize) + { + source.PageSize = configuration.PagingConfiguration.MaxAvailablePageSize; + } + + // + return source; } } } \ No newline at end of file