Files
xDataService/Extensions/IQueryableExtensions.cs
T
2024-01-25 04:41:17 +03:30

503 lines
16 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using xCommons.Extensions;
using xDataService.Constants;
using xExceptions.Constants;
using xModels.Base;
namespace xDataService.Extensions {
public static class IQueryableExtensions {
/// <summary>
/// Apply Search Filter on a Query
/// </summary>
/// <param name="source"></param>
/// <param name="filter"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IQueryable<T> ApplyFilter<T, TKey> (
this IQueryable<T> source,
string filter
)
where T : XBaseEntity<TKey> {
//
// Apply Filter ...
return source.Where (r => r.PropValuesContains (filter));
}
/// <summary>
/// Apply Search Filter on a Query
/// </summary>
/// <param name="source"></param>
/// <param name="filter"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static async Task<IQueryable<T>> ApplyFilterAsync<T, TKey> (
this IQueryable<T> source,
string filter
)
where T : XBaseEntity<TKey> {
//
// Where Clause ...
Expression<Func<T, bool>> whereClause = r => r
.PropValuesContains (filter);
//
// Generate Where Function ...
var whereFunc = whereClause.Compile ();
//
// Get Enumerable ...
var enumerator = source
.AsAsyncEnumerable ();
//
var result = new List<T> ();
await
foreach (var entity in enumerator) {
//
var isApproved = whereFunc (entity);
if (isApproved) {
result.Add (entity);
}
}
//
return result.AsQueryable ();
}
/// <summary>
/// Apply Search Filter on a Enumerable
/// </summary>
/// <param name="source"></param>
/// <param name="filter"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static async Task<IEnumerable<T>> ApplyFilterAsync<T, TKey> (
this IEnumerable<T> source,
string filter
)
where T : XBaseEntity<TKey> {
//
// Where Clause ...
Expression<Func<T, bool>> whereClause = r => r
.PropValuesContains (filter);
//
// Generate Where Function ...
var whereFunc = whereClause.Compile ();
//
// Get Enumerable ...
var enumerator = source
.AsQueryable ()
.AsAsyncEnumerable ();
//
var result = new List<T> ();
await
foreach (var entity in enumerator) {
//
var isApproved = whereFunc (entity);
if (isApproved) {
result.Add (entity);
}
}
//
return result
.AsEnumerable ();
}
/// <summary>
/// Apply Search Filter on a Enumerable
/// </summary>
/// <param name="source"></param>
/// <param name="filter"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IEnumerable<T> ApplyFilter<T> (
this IEnumerable<T> source,
string filter
)
where T : class {
//
// Apply Filter ...
return source.Where (r => r.PropValuesContains (filter));
}
/// <summary>
/// provide a way to Order an IQueryable by string FieldName ...
/// </summary>
/// <param name="query"></param>
/// <param name="propertyName"></param>
/// <typeparam name="TSource"></typeparam>
/// <returns></returns>
public static IOrderedQueryable<TSource> OrderBy<TSource> (
this IQueryable<TSource> query,
string propertyName
) {
//
var entityType = typeof (TSource);
//
// Create x => x.PropName
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 });
//
// 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 parameters = m.GetParameters ().ToList ();
//
// Put more restriction here to ensure selecting the right overload
return parameters.Count == 2; // overload that has 2 parameters
}).Single ();
//
// The linq's OrderBy<TSource, TKey> has two generic types, which provided here ...
MethodInfo genericMethod = method
.MakeGenericMethod (entityType, propertyInfo.PropertyType);
/*
Call query.OrderBy(selector), with query and selector: x=> x.PropName
Note that we pass the selector as Expression to the method and we don't compile it.
By doing so EF can extract "order by" columns and generate SQL for it.
*/
var newQuery = (IOrderedQueryable<TSource>) genericMethod
.Invoke (genericMethod, new object[] { query, selector });
//
return newQuery;
}
/// <summary>
/// provide a way to Order an IEnumerable by string FieldName ...
/// </summary>
/// <param name="query"></param>
/// <param name="propertyName"></param>
/// <typeparam name="TSource"></typeparam>
/// <returns></returns>
public static IOrderedEnumerable<TSource> OrderBy<TSource> (
this IEnumerable<TSource> query,
string propertyName
) {
//
var entityType = typeof (TSource);
var propertyInfo = entityType.GetProperty (propertyName);
if (propertyInfo.IsNull ()) {
XException.InvalidArgs.Throw ();
}
//
return query.OrderBy (x => propertyInfo.GetValue (x, null));
}
/// <summary>
/// provide a way to Order an IQueryable by string FieldName ...
/// </summary>
/// <param name="query"></param>
/// <param name="propertyName"></param>
/// <typeparam name="TSource"></typeparam>
/// <returns></returns>
public static IOrderedQueryable<TSource> OrderByDescending<TSource> (
this IQueryable<TSource> query,
string propertyName
) {
//
var entityType = typeof (TSource);
//
// Create x => x.PropName
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 });
//
// 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 parameters = m.GetParameters ().ToList ();
//
// Put more restriction here to ensure selecting the right overload
return parameters.Count == 2; // overload that has 2 parameters
}).Single ();
//
// The linq's OrderBy<TSource, TKey> has two generic types, which provided here ...
MethodInfo genericMethod = method
.MakeGenericMethod (entityType, propertyInfo.PropertyType);
/*
Call query.OrderBy(selector), with query and selector: x=> x.PropName
Note that we pass the selector as Expression to the method and we don't compile it.
By doing so EF can extract "order by" columns and generate SQL for it.
*/
var newQuery = (IOrderedQueryable<TSource>) genericMethod
.Invoke (genericMethod, new object[] { query, selector });
//
return newQuery;
}
/// <summary>
/// provide a way to Order an IEnumerable by string FieldName ...
/// </summary>
/// <param name="query"></param>
/// <param name="propertyName"></param>
/// <typeparam name="TSource"></typeparam>
/// <returns></returns>
public static IOrderedEnumerable<TSource> OrderByDescending<TSource> (
this IEnumerable<TSource> query,
string propertyName
) {
//
var entityType = typeof (TSource);
var propertyInfo = entityType.GetProperty (propertyName);
if (propertyInfo.IsNull ()) {
XException.InvalidArgs.Throw ();
}
//
return query.OrderByDescending (x => propertyInfo.GetValue (x, null));
}
/// <summary>
/// Apply Sorting on a query
/// </summary>
/// <param name="source"></param>
/// <param name="sortBy"></param>
/// <param name="isAscending"></param>
/// <param name="columnsMap"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IQueryable<T> ApplySorting<T, TKey> (
this IQueryable<T> source,
string sortBy,
bool isAscending,
IDictionary<string, Expression<Func<T, object>>> columnsMap = null
)
where T : XBaseEntity<TKey> {
//
if (columnsMap == null) {
//
columnsMap = source.FirstOrDefault ()
.GetDefaultColumnsMap ();
//
if (columnsMap == null) {
return source;
}
}
//
// Validate SortBy ...
var sortByItem = columnsMap.Keys
.FirstOrDefault (p =>
p.ToNormalString () == sortBy.ToNormalString ());
if (sortByItem.IsNullOrEmpty ()) {
return source;
}
//
// Apply Sorting ...
if (isAscending) {
return source.OrderBy (sortByItem);
} else {
return source.OrderByDescending (sortByItem);
}
}
/// <summary>
/// Apply Sorting on a query
/// </summary>
/// <param name="source"></param>
/// <param name="sortBy"></param>
/// <param name="isAscending"></param>
/// <param name="columnsMap"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IEnumerable<T> ApplySorting<T, TKey> (
this IEnumerable<T> source,
string sortBy,
bool isAscending,
IDictionary<string, Expression<Func<T, object>>> columnsMap = null
)
where T : XBaseEntity<TKey> {
//
if (columnsMap == null) {
//
columnsMap = source.FirstOrDefault ()
.GetDefaultColumnsMap ();
//
if (columnsMap == null) {
return source;
}
}
//
// Validate SortBy ...
var sortByItem = columnsMap.Keys
.FirstOrDefault (p =>
p.ToNormalString () == sortBy.ToNormalString ());
if (sortByItem.IsNullOrEmpty ()) {
return source;
}
//
// Apply Sorting ...
if (isAscending) {
return source.OrderBy (sortByItem);
} else {
return source.OrderByDescending (sortByItem);
}
}
/// <summary>
/// Apply Sorting on a Enumerable
/// </summary>
/// <param name="source"></param>
/// <param name="sortBy"></param>
/// <param name="isAscending"></param>
/// <param name="columnsMap"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IEnumerable<T> ApplySorting<T> (
this IEnumerable<T> source,
string sortBy,
bool isAscending,
IDictionary<string, Expression<Func<T, object>>> columnsMap = null
)
where T : class {
//
if (columnsMap == null) {
//
columnsMap = source.FirstOrDefault ()
.GetDefaultColumnsMap ();
//
if (columnsMap == null) {
return source;
}
}
//
// Validate SortBy ...
var sortByItem = columnsMap.Keys
.FirstOrDefault (p =>
p.ToNormalString () == sortBy.ToNormalString ());
if (sortByItem.IsNullOrEmpty ()) {
return source;
}
//
// Apply Sorting ...
if (isAscending) {
return source.OrderBy (sortByItem);
} else {
return source.OrderByDescending (sortByItem);
}
}
/// <summary>
/// Apply Paging on a qury
/// </summary>
/// <param name="source"></param>
/// <param name="page"></param>
/// <param name="pageSize"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IQueryable<T> ApplyPaging<T> (
this IQueryable<T> source,
int? page,
int? pageSize
) {
//
if (!page.HasValue ||
page <= 0) {
page = 1;
}
//
if (!pageSize.HasValue ||
pageSize <= 0 ||
pageSize > XDataServiceConstants.MAX_AVAILABLE_PAGE_SIZE) {
pageSize = XDataServiceConstants.DEFAULT_PAGE_SIZE;
}
//
var pageVal = page.GetValueOrDefault ();
var pageSizeVal = pageSize.GetValueOrDefault ();
//
return source
.Skip ((pageVal - 1) * pageSizeVal)
.Take (pageSizeVal);
}
/// <summary>
/// Apply Paging on a Enumerable
/// </summary>
/// <param name="source"></param>
/// <param name="page"></param>
/// <param name="pageSize"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IEnumerable<T> ApplyPaging<T> (
this IEnumerable<T> source,
int? page,
int? pageSize
) {
//
if (!page.HasValue ||
page <= 0) {
page = 1;
}
//
if (!pageSize.HasValue ||
pageSize <= 0 ||
pageSize > XDataServiceConstants.MAX_AVAILABLE_PAGE_SIZE) {
pageSize = XDataServiceConstants.DEFAULT_PAGE_SIZE;
}
//
var pageVal = page.GetValueOrDefault ();
var pageSizeVal = pageSize.GetValueOrDefault ();
//
return source
.Skip ((pageVal - 1) * pageSizeVal)
.Take (pageSizeVal);
}
}
}