2025-02-13 13:35:41 +02:00
using Radzen ;
using Radzen.Blazor ;
2021-01-19 11:02:49 +02:00
using System ;
using System.Collections ;
using System.Collections.Generic ;
2023-05-05 15:54:10 +03:00
using System.Globalization ;
2021-01-19 11:02:49 +02:00
using System.Linq ;
2025-02-13 13:35:41 +02:00
using System.Linq.Expressions ;
using System.Reflection ;
2025-02-14 10:28:35 +02:00
using System.Text.RegularExpressions ;
2021-01-19 11:02:49 +02:00
namespace Radzen
{
2021-10-07 11:35:29 +03:00
/// <summary>
/// Class QueryableExtension.
/// </summary>
2021-01-19 11:02:49 +02:00
public static class QueryableExtension
{
2025-02-13 13:35:41 +02:00
static Expression notNullCheck ( Expression property ) = > Nullable . GetUnderlyingType ( property . Type ) ! = null | | property . Type = = typeof ( string ) ?
2025-02-14 14:51:09 +02:00
Expression . Coalesce ( property , property . Type = = typeof ( string ) ? Expression . Constant ( string . Empty ) : Expression . Constant ( null , property . Type ) ) : property ;
2025-02-13 13:35:41 +02:00
/// <summary>
/// Projects each element of a sequence into a collection of property values.
/// </summary>
2025-03-06 10:30:58 +02:00
internal static IQueryable Select ( this IQueryable source , string propertyName )
2025-02-13 13:35:41 +02:00
{
var parameter = Expression . Parameter ( source . ElementType , "x" ) ;
var property = GetNestedPropertyExpression ( parameter , propertyName ) ;
var lambda = Expression . Lambda ( notNullCheck ( property ) , parameter ) ;
var selectExpression = Expression . Call ( typeof ( Queryable ) ,
nameof ( Queryable . Select ) , [ source . ElementType , property . Type ] , source . Expression ,
Expression . Quote ( lambda ) ) ;
return source . Provider . CreateQuery ( selectExpression ) ;
}
/// <summary>
/// Projects each element of a sequence to an IEnumerable and flattens the resulting sequences into one sequence.
/// </summary>
public static IQueryable SelectMany ( this IQueryable source , string propertyName )
{
var parameter = Expression . Parameter ( source . ElementType , "x" ) ;
var property = GetNestedPropertyExpression ( parameter , propertyName ) ;
var lambda = Expression . Lambda ( notNullCheck ( property ) , parameter ) ;
var returnElementType = property . Type . GetElementType ( ) ? ?
( property . Type . IsGenericType ? property . Type . GetGenericArguments ( ) [ 0 ] : typeof ( object ) ) ;
var enumerableType = typeof ( IEnumerable < > ) . MakeGenericType ( returnElementType ) ;
var delegateType = typeof ( Func < , > ) . MakeGenericType ( source . ElementType , enumerableType ) ;
lambda = Expression . Lambda ( delegateType , lambda . Body , lambda . Parameters ) ;
var selectManyExpression = Expression . Call ( typeof ( Queryable ) ,
nameof ( Queryable . SelectMany ) , [ source . ElementType , returnElementType ] , source . Expression ,
Expression . Quote ( lambda ) ) ;
return source . Provider . CreateQuery ( selectManyExpression ) ;
}
/// <summary>
/// Projects each element of a sequence to an IEnumerable and flattens the resulting sequences into one sequence.
/// </summary>
public static IQueryable < GroupResult > GroupByMany < T > ( this IQueryable < T > source , string [ ] properties )
{
var parameter = Expression . Parameter ( source . ElementType , "x" ) ;
return GroupByMany ( source ,
2025-05-07 13:11:30 +03:00
properties . Select ( p = > Expression . Lambda < Func < T , object > > ( Expression . Convert ( GetNestedPropertyExpression ( parameter , p ) , typeof ( object ) ) , parameter ) . Compile ( ) ) . ToArray ( ) ,
2025-02-13 13:35:41 +02:00
0 ) ;
}
private static IQueryable < GroupResult > GroupByMany < T > ( IEnumerable < T > source , Func < T , object > [ ] lambdas , int index )
{
return source . GroupBy ( lambdas [ index ] ) . Select (
g = > new GroupResult
{
Key = g . Key ,
Count = g . Count ( ) ,
Items = g ,
Subgroups = index < lambdas . Length - 1 ? GroupByMany ( g , lambdas , index + 1 ) : null
} ) . AsQueryable ( ) ;
}
2025-02-19 17:58:20 +02:00
internal static string RemoveVariableReference ( string expression )
{
// Regex pattern to match any variable reference in a lambda expression
string pattern = @"^\s*\b\w+\b\s*=>\s*" ; // Matches "it => " or similar
// Remove the variable reference from the start
expression = Regex . Replace ( expression , pattern , "" ) . Trim ( ) ;
// Remove remaining instances of the variable reference prefix (e.g., "it.")
pattern = @"\b\w+\." ; // Matches "it.", "x.", etc.
expression = Regex . Replace ( expression , pattern , "" ) ;
return expression . Trim ( ) ;
}
2025-02-13 13:35:41 +02:00
/// <summary>
/// Sorts the elements of a sequence in ascending or descending order according to a key.
/// </summary>
2025-02-19 18:09:44 +02:00
/// <returns>A <see cref="IQueryable{T}"/> whose elements are sorted according to the specified <paramref name="selector"/>.</returns>
2025-02-19 17:58:20 +02:00
public static IOrderedQueryable < T > OrderBy < T > ( this IQueryable < T > source , string selector = null )
2025-02-13 13:35:41 +02:00
{
2025-04-24 13:04:31 +03:00
selector = $"{selector}" ;
if ( selector . Contains ( "=>" ) )
{
var identifierName = selector . Split ( "=>" ) [ 0 ] ;
selector = selector . Replace ( $"{identifierName}=>" , "" ) . Trim ( ) ;
string methodAsc = "OrderBy" ;
string methodDesc = "OrderByDescending" ;
Expression expression = source . Expression ;
foreach ( var part in selector . Split ( "," ) )
{
var lambda = ExpressionParser . ParseLambda < T > ( $"{identifierName.Trim()} => {part}" ) ;
expression = Expression . Call (
typeof ( Queryable ) , part . Trim ( ) . ToLower ( ) . Contains ( " desc" ) ? methodDesc : methodAsc ,
new Type [ ] { source . ElementType , lambda . ReturnType } ,
expression , Expression . Quote ( lambda ) ) ;
methodAsc = "ThenBy" ;
methodDesc = "ThenByDescending" ;
}
return ( IOrderedQueryable < T > ) source . Provider . CreateQuery ( expression ) ;
}
2025-02-19 17:58:20 +02:00
return ( IOrderedQueryable < T > ) OrderBy ( ( IQueryable ) source , selector ) ;
2025-02-13 13:35:41 +02:00
}
/// <summary>
/// Sorts the elements of a sequence in ascending or descending order according to a key.
/// </summary>
2025-02-19 18:09:44 +02:00
/// <returns>A <see cref="IQueryable"/> whose elements are sorted according to the specified <paramref name="selector"/>.</returns>
2025-02-19 17:58:20 +02:00
public static IQueryable OrderBy ( this IQueryable source , string selector = null )
2025-02-13 13:35:41 +02:00
{
2025-02-19 17:58:20 +02:00
selector = selector . Contains ( "=>" ) ? RemoveVariableReference ( selector ) : selector ;
2025-02-13 13:35:41 +02:00
var parameters = new ParameterExpression [ ] { Expression . Parameter ( source . ElementType , "x" ) } ;
Expression expression = source . Expression ;
string methodAsc = "OrderBy" ;
string methodDesc = "OrderByDescending" ;
2025-03-07 11:14:15 +02:00
string [ ] sortStrings = new string [ ] { "asc" , "desc" } ;
2025-02-13 13:35:41 +02:00
2025-02-19 17:58:20 +02:00
foreach ( var o in ( selector ? ? "" ) . Split ( ',' , StringSplitOptions . RemoveEmptyEntries ) )
2025-02-13 13:35:41 +02:00
{
var nameAndOrder = o . Trim ( ) ;
2025-03-07 11:14:15 +02:00
var name = string . Join ( " " , nameAndOrder . Split ( ' ' ) . Where ( i = > ! sortStrings . Contains ( i . Trim ( ) ) ) ) . Trim ( ) ;
var order = nameAndOrder . Split ( ' ' ) . FirstOrDefault ( i = > sortStrings . Contains ( i . Trim ( ) ) ) ? ? sortStrings . First ( ) ;
2025-02-13 13:35:41 +02:00
Expression property = ! string . IsNullOrEmpty ( nameAndOrder ) ?
2025-03-06 21:09:26 +02:00
GetNestedPropertyExpression ( parameters . FirstOrDefault ( ) , name ) : parameters . FirstOrDefault ( ) ;
2025-02-13 13:35:41 +02:00
expression = Expression . Call (
2025-03-07 11:14:15 +02:00
typeof ( Queryable ) , order . Equals ( sortStrings . First ( ) , StringComparison . OrdinalIgnoreCase ) ? methodAsc : methodDesc ,
2025-02-13 13:35:41 +02:00
new Type [ ] { source . ElementType , property . Type } ,
2025-03-21 07:04:17 +02:00
expression , Expression . Quote ( Expression . Lambda ( property , parameters ) ) ) ;
2025-02-13 13:35:41 +02:00
methodAsc = "ThenBy" ;
methodDesc = "ThenByDescending" ;
}
return source . Provider . CreateQuery ( expression ) ;
}
/// <summary>
/// Returns the first element of a sequence, or a default value if the sequence contains no elements.
/// </summary>
/// <param name="source">The <see cref="IQueryable"/> to return the first element of.</param>
/// <returns>default if source is empty; otherwise, the first element in source.</returns>
public static dynamic FirstOrDefault ( this IQueryable source )
{
return source . Provider . Execute ( Expression . Call ( null ,
typeof ( Queryable ) . GetTypeInfo ( ) . GetDeclaredMethods ( nameof ( Queryable . FirstOrDefault ) ) . FirstOrDefault ( mi = > mi . IsGenericMethod ) . MakeGenericMethod ( source . ElementType ) ,
source . Expression ) ) ;
}
/// <summary>
/// Converts the elements of an <see cref="IQueryable"/> to the specified type.
/// </summary>
/// <param name="source">The <see cref="IQueryable"/> that contains the elements to be converted.</param>
/// <param name="type">The type to convert the elements of source to.</param>
/// <returns>An <see cref="IQueryable"/> that contains each element of the source sequence converted to the specified type.</returns>
public static IQueryable Cast ( this IQueryable source , Type type )
{
2025-05-07 13:11:30 +03:00
return source . Provider . CreateQuery ( Expression . Call ( null ,
typeof ( Queryable ) . GetTypeInfo ( ) . GetDeclaredMethods ( nameof ( Queryable . Cast ) ) . FirstOrDefault ( mi = > mi . IsGenericMethod ) . MakeGenericMethod ( type ) ,
2025-02-13 13:35:41 +02:00
source . Expression ) ) ;
}
/// <summary>
/// Returns distinct elements from a sequence by using the default equality comparer to compare values.
/// </summary>
/// <param name="source">The sequence to remove duplicate elements from.</param>
/// <returns>An <see cref="IQueryable"/> that contains distinct elements from the source sequence.</returns>
public static IQueryable Distinct ( this IQueryable source )
{
return source . Provider . CreateQuery ( Expression . Call ( null ,
typeof ( Queryable ) . GetTypeInfo ( ) . GetDeclaredMethods ( nameof ( Queryable . Distinct ) ) . FirstOrDefault ( mi = > mi . IsGenericMethod ) . MakeGenericMethod ( source . ElementType ) ,
source . Expression ) ) ;
}
/// <summary>
/// Filters using the specified filter descriptors.
/// </summary>
public static IQueryable Where (
this IQueryable source ,
IEnumerable < FilterDescriptor > filters ,
LogicalFilterOperator logicalFilterOperator ,
FilterCaseSensitivity filterCaseSensitivity )
{
var whereMethod = typeof ( QueryableExtension )
. GetMethods ( )
. First ( m = > m . Name = = "Where" & & m . IsGenericMethodDefinition & & m . GetParameters ( ) . Any ( p = > p . ParameterType = = typeof ( IEnumerable < FilterDescriptor > ) ) )
. MakeGenericMethod ( source . ElementType ) ;
return ( IQueryable ) whereMethod . Invoke ( null , new object [ ] { source , filters , logicalFilterOperator , filterCaseSensitivity } ) ;
}
/// <summary>
/// Filters using the specified filter descriptors.
/// </summary>
2025-05-07 13:11:30 +03:00
public static IQueryable < T > Where < T > ( this IQueryable < T > source , IEnumerable < FilterDescriptor > filters ,
2025-02-13 13:35:41 +02:00
LogicalFilterOperator logicalFilterOperator , FilterCaseSensitivity filterCaseSensitivity )
{
if ( filters = = null | | ! filters . Any ( ) )
return source ;
var parameter = Expression . Parameter ( typeof ( T ) , "x" ) ;
Expression combinedExpression = null ;
foreach ( var filter in filters )
{
var expression = GetExpression < T > ( parameter , filter , filterCaseSensitivity , filter . Type ) ;
if ( expression = = null ) continue ;
combinedExpression = combinedExpression = = null
? expression
: logicalFilterOperator = = LogicalFilterOperator . And ?
Expression . AndAlso ( combinedExpression , expression ) :
Expression . OrElse ( combinedExpression , expression ) ;
}
if ( combinedExpression = = null )
return source ;
var lambda = Expression . Lambda < Func < T , bool > > ( combinedExpression , parameter ) ;
return source . Where ( lambda ) ;
}
internal static Expression GetNestedPropertyExpression ( Expression expression , string property , Type type = null )
{
var parts = property . Split ( new char [ ] { '.' } , 2 ) ;
2025-02-13 14:28:08 +02:00
string currentPart = parts [ 0 ] ;
2025-02-13 13:35:41 +02:00
Expression member ;
2025-02-13 14:28:08 +02:00
2025-05-15 07:28:10 +03:00
if ( expression . Type . IsGenericType & & typeof ( IDictionary < , > ) . IsAssignableFrom ( expression . Type . GetGenericTypeDefinition ( ) ) )
2025-02-13 13:35:41 +02:00
{
2025-02-13 14:28:08 +02:00
var key = currentPart . Split ( '"' ) [ 1 ] ;
var typeString = currentPart . Split ( '(' ) [ 0 ] ;
2025-02-13 13:35:41 +02:00
2025-05-12 12:35:46 +03:00
var indexer = Expression . Property ( expression , expression . Type . GetProperty ( "Item" ) , Expression . Constant ( key ) ) ;
2025-02-13 13:35:41 +02:00
member = Expression . Convert (
2025-05-12 12:35:46 +03:00
indexer ,
parts . Length > 1 ? indexer . Type : type ? ? Type . GetType ( typeString . EndsWith ( "?" ) ? $"System.Nullable`1[System.{typeString.TrimEnd('?')}]" : $"System.{typeString}" ) ? ? typeof ( object ) ) ;
2025-02-13 14:28:08 +02:00
}
else if ( currentPart . Contains ( "[" ) ) // Handle array or list indexing
{
var indexStart = currentPart . IndexOf ( '[' ) ;
var propertyName = currentPart . Substring ( 0 , indexStart ) ;
var indexString = currentPart . Substring ( indexStart + 1 , currentPart . Length - indexStart - 2 ) ;
member = Expression . PropertyOrField ( expression , propertyName ) ;
if ( int . TryParse ( indexString , out int index ) )
{
if ( member . Type . IsArray )
{
member = Expression . ArrayIndex ( member , Expression . Constant ( index ) ) ;
}
else if ( member . Type . IsGenericType & &
( member . Type . GetGenericTypeDefinition ( ) = = typeof ( List < > ) | |
typeof ( IList < > ) . IsAssignableFrom ( member . Type . GetGenericTypeDefinition ( ) ) ) )
{
var itemProperty = member . Type . GetProperty ( "Item" ) ;
if ( itemProperty ! = null )
{
member = Expression . Property ( member , itemProperty , Expression . Constant ( index ) ) ;
}
}
}
else
{
throw new ArgumentException ( $"Invalid index format: {indexString}" ) ;
}
2025-02-13 13:35:41 +02:00
}
2025-03-06 21:03:35 +02:00
else if ( expression . Type . IsInterface )
{
member = Expression . Property ( expression ,
new [ ] { expression . Type } . Concat ( expression . Type . GetInterfaces ( ) ) . FirstOrDefault ( t = > t . GetProperty ( currentPart ) ! = null ) ,
currentPart
) ;
}
2025-02-13 13:35:41 +02:00
else
{
2025-02-13 14:28:08 +02:00
member = Expression . PropertyOrField ( expression , currentPart ) ;
2025-02-13 13:35:41 +02:00
}
2025-02-17 12:56:57 +02:00
if ( expression . Type . IsValueType & & Nullable . GetUnderlyingType ( expression . Type ) = = null )
{
expression = Expression . Convert ( expression , typeof ( object ) ) ;
}
2025-02-13 13:35:41 +02:00
return parts . Length > 1 ? GetNestedPropertyExpression ( member , parts [ 1 ] , type ) :
( Nullable . GetUnderlyingType ( member . Type ) ! = null | | member . Type = = typeof ( string ) ) ?
Expression . Condition ( Expression . Equal ( expression , Expression . Constant ( null ) ) , Expression . Constant ( null , member . Type ) , member ) :
2025-02-13 14:28:08 +02:00
member ;
2025-02-13 13:35:41 +02:00
}
internal static Expression GetExpression < T > ( ParameterExpression parameter , FilterDescriptor filter , FilterCaseSensitivity filterCaseSensitivity , Type type )
{
Type valueType = filter . FilterValue ! = null ? filter . FilterValue . GetType ( ) : null ;
2025-02-14 17:58:04 +02:00
var isEnumerable = valueType ! = null & & IsEnumerable ( valueType ) & & valueType ! = typeof ( string ) ;
2025-02-13 13:35:41 +02:00
Type secondValueType = filter . SecondFilterValue ! = null ? filter . SecondFilterValue . GetType ( ) : null ;
2025-02-18 23:10:17 +02:00
Expression p = GetNestedPropertyExpression ( parameter , filter . Property , type ) ;
Expression property = GetNestedPropertyExpression ( parameter , ! isEnumerable & & ! IsEnumerable ( p . Type ) ? filter . FilterProperty ? ? filter . Property : filter . Property , type ) ;
2025-02-13 13:35:41 +02:00
Type collectionItemType = IsEnumerable ( property . Type ) & & property . Type . IsGenericType ? property . Type . GetGenericArguments ( ) [ 0 ] : null ;
ParameterExpression collectionItemTypeParameter = collectionItemType ! = null ? Expression . Parameter ( collectionItemType , "x" ) : null ;
if ( collectionItemType ! = null & & ! string . IsNullOrEmpty ( filter . FilterProperty ) & & filter . Property ! = filter . FilterProperty )
{
property = GetNestedPropertyExpression ( collectionItemTypeParameter , filter . FilterProperty ) ;
filter . FilterOperator = filter . FilterOperator = = FilterOperator . In ? FilterOperator . Contains :
filter . FilterOperator = = FilterOperator . NotIn ? FilterOperator . DoesNotContain : filter . FilterOperator ;
}
2025-02-17 16:35:57 +02:00
var isEnum = ! isEnumerable & & ( PropertyAccess . IsEnum ( property . Type ) | | PropertyAccess . IsNullableEnum ( property . Type ) ) ;
2025-02-14 17:58:04 +02:00
var caseInsensitive = property . Type = = typeof ( string ) & & ! isEnumerable & & filterCaseSensitivity = = FilterCaseSensitivity . CaseInsensitive ;
2025-02-13 13:35:41 +02:00
2025-03-04 08:46:10 +02:00
var isEnumerableProperty = IsEnumerable ( property . Type ) & & property . Type ! = typeof ( string ) ;
2025-02-13 13:35:41 +02:00
var constant = Expression . Constant ( caseInsensitive ?
$"{filter.FilterValue}" . ToLowerInvariant ( ) :
2025-02-17 16:35:57 +02:00
isEnum & & ! isEnumerable & & filter . FilterValue ! = null ? Enum . ToObject ( Nullable . GetUnderlyingType ( property . Type ) ? ? property . Type , filter . FilterValue ) : filter . FilterValue ,
2025-03-04 08:46:10 +02:00
! isEnum & & isEnumerable ? valueType : isEnumerableProperty ? valueType : property . Type ) ;
2025-02-13 13:35:41 +02:00
2025-02-14 17:58:04 +02:00
if ( caseInsensitive & & ! isEnumerable )
2025-02-13 13:35:41 +02:00
{
2025-02-17 19:05:06 +02:00
property = Expression . Call ( notNullCheck ( property ) , typeof ( string ) . GetMethod ( "ToLower" , System . Type . EmptyTypes ) ) ;
2025-02-13 13:35:41 +02:00
}
var secondConstant = filter . SecondFilterValue ! = null ?
Expression . Constant ( caseInsensitive ?
$"{filter.SecondFilterValue}" . ToLowerInvariant ( ) :
isEnum & & filter . SecondFilterValue ! = null ? Enum . ToObject ( Nullable . GetUnderlyingType ( property . Type ) ? ? property . Type , filter . SecondFilterValue ) : filter . SecondFilterValue ,
secondValueType ! = null & & ! isEnum & & IsEnumerable ( secondValueType ) ? secondValueType : property . Type ) : null ;
Expression primaryExpression = filter . FilterOperator switch
{
FilterOperator . Equals = > Expression . Equal ( notNullCheck ( property ) , constant ) ,
FilterOperator . NotEquals = > Expression . NotEqual ( notNullCheck ( property ) , constant ) ,
FilterOperator . LessThan = > Expression . LessThan ( notNullCheck ( property ) , constant ) ,
FilterOperator . LessThanOrEquals = > Expression . LessThanOrEqual ( notNullCheck ( property ) , constant ) ,
FilterOperator . GreaterThan = > Expression . GreaterThan ( notNullCheck ( property ) , constant ) ,
FilterOperator . GreaterThanOrEquals = > Expression . GreaterThanOrEqual ( notNullCheck ( property ) , constant ) ,
2025-02-14 17:58:04 +02:00
FilterOperator . Contains = > isEnumerable ?
2025-02-20 10:59:26 +02:00
Expression . Call ( typeof ( Enumerable ) , nameof ( Enumerable . Contains ) , new Type [ ] { property . Type } , constant , notNullCheck ( property ) ) :
2025-03-04 08:46:10 +02:00
isEnumerableProperty ?
Expression . Call ( typeof ( Enumerable ) , nameof ( Enumerable . Contains ) , new Type [ ] { collectionItemType } , notNullCheck ( property ) , constant ) :
Expression . Call ( notNullCheck ( property ) , typeof ( string ) . GetMethod ( "Contains" , new [ ] { typeof ( string ) } ) , constant ) ,
2025-02-14 17:58:04 +02:00
FilterOperator . In = > isEnumerable & &
2025-03-04 08:46:10 +02:00
isEnumerableProperty ?
2025-02-20 10:59:26 +02:00
Expression . Call ( typeof ( Enumerable ) , nameof ( Enumerable . Any ) , new Type [ ] { collectionItemType } ,
Expression . Call ( typeof ( Enumerable ) , nameof ( Enumerable . Intersect ) , new Type [ ] { collectionItemType } , constant , notNullCheck ( property ) ) ) : Expression . Constant ( true ) ,
2025-02-14 17:58:04 +02:00
FilterOperator . DoesNotContain = > isEnumerable ?
2025-02-20 10:59:26 +02:00
Expression . Not ( Expression . Call ( typeof ( Enumerable ) , nameof ( Enumerable . Contains ) , new Type [ ] { property . Type } , constant , notNullCheck ( property ) ) ) :
2025-03-04 08:46:10 +02:00
isEnumerableProperty ?
Expression . Not ( Expression . Call ( typeof ( Enumerable ) , nameof ( Enumerable . Contains ) , new Type [ ] { collectionItemType } , notNullCheck ( property ) , constant ) ) :
Expression . Not ( Expression . Call ( notNullCheck ( property ) , typeof ( string ) . GetMethod ( "Contains" , new [ ] { typeof ( string ) } ) , constant ) ) ,
2025-02-14 17:58:04 +02:00
FilterOperator . NotIn = > isEnumerable & &
2025-03-04 08:46:10 +02:00
isEnumerableProperty ?
2025-02-20 10:59:26 +02:00
Expression . Call ( typeof ( Enumerable ) , nameof ( Enumerable . Any ) , new Type [ ] { collectionItemType } ,
Expression . Call ( typeof ( Enumerable ) , nameof ( Enumerable . Except ) , new Type [ ] { collectionItemType } , constant , notNullCheck ( property ) ) ) : Expression . Constant ( true ) ,
2025-02-13 13:35:41 +02:00
FilterOperator . StartsWith = > Expression . Call ( notNullCheck ( property ) , typeof ( string ) . GetMethod ( "StartsWith" , new [ ] { typeof ( string ) } ) , constant ) ,
FilterOperator . EndsWith = > Expression . Call ( notNullCheck ( property ) , typeof ( string ) . GetMethod ( "EndsWith" , new [ ] { typeof ( string ) } ) , constant ) ,
FilterOperator . IsNull = > Expression . Equal ( property , Expression . Constant ( null , property . Type ) ) ,
FilterOperator . IsNotNull = > Expression . NotEqual ( property , Expression . Constant ( null , property . Type ) ) ,
FilterOperator . IsEmpty = > Expression . Equal ( property , Expression . Constant ( String . Empty ) ) ,
FilterOperator . IsNotEmpty = > Expression . NotEqual ( property , Expression . Constant ( String . Empty ) ) ,
_ = > null
} ;
if ( collectionItemType ! = null & & primaryExpression ! = null & &
! ( filter . FilterOperator = = FilterOperator . In | | filter . FilterOperator = = FilterOperator . NotIn ) )
{
primaryExpression = Expression . Call ( typeof ( Enumerable ) , nameof ( Enumerable . Any ) , new Type [ ] { collectionItemType } ,
GetNestedPropertyExpression ( parameter , filter . Property ) , Expression . Lambda ( primaryExpression , collectionItemTypeParameter ) ) ;
}
Expression secondExpression = null ;
if ( secondConstant ! = null )
{
secondExpression = filter . SecondFilterOperator switch
{
FilterOperator . Equals = > Expression . Equal ( notNullCheck ( property ) , secondConstant ) ,
FilterOperator . NotEquals = > Expression . NotEqual ( notNullCheck ( property ) , secondConstant ) ,
FilterOperator . LessThan = > Expression . LessThan ( notNullCheck ( property ) , secondConstant ) ,
FilterOperator . LessThanOrEquals = > Expression . LessThanOrEqual ( notNullCheck ( property ) , secondConstant ) ,
FilterOperator . GreaterThan = > Expression . GreaterThan ( notNullCheck ( property ) , secondConstant ) ,
FilterOperator . GreaterThanOrEquals = > Expression . GreaterThanOrEqual ( notNullCheck ( property ) , secondConstant ) ,
2025-02-14 16:03:31 +02:00
FilterOperator . Contains = > Expression . Call ( notNullCheck ( property ) , typeof ( string ) . GetMethod ( "Contains" , new [ ] { typeof ( string ) } ) , secondConstant ) ,
2025-02-13 13:35:41 +02:00
FilterOperator . DoesNotContain = > Expression . Not ( Expression . Call ( notNullCheck ( property ) , property . Type . GetMethod ( "Contains" , new [ ] { typeof ( string ) } ) , secondConstant ) ) ,
FilterOperator . StartsWith = > Expression . Call ( notNullCheck ( property ) , typeof ( string ) . GetMethod ( "StartsWith" , new [ ] { typeof ( string ) } ) , secondConstant ) ,
FilterOperator . EndsWith = > Expression . Call ( notNullCheck ( property ) , typeof ( string ) . GetMethod ( "EndsWith" , new [ ] { typeof ( string ) } ) , secondConstant ) ,
FilterOperator . IsNull = > Expression . Equal ( property , Expression . Constant ( null , property . Type ) ) ,
FilterOperator . IsNotNull = > Expression . NotEqual ( property , Expression . Constant ( null , property . Type ) ) ,
FilterOperator . IsEmpty = > Expression . Equal ( property , Expression . Constant ( String . Empty ) ) ,
FilterOperator . IsNotEmpty = > Expression . NotEqual ( property , Expression . Constant ( String . Empty ) ) ,
_ = > null
} ;
}
if ( collectionItemType ! = null & & secondExpression ! = null & &
! ( filter . SecondFilterOperator = = FilterOperator . In | | filter . SecondFilterOperator = = FilterOperator . NotIn ) )
{
secondExpression = Expression . Call ( typeof ( Enumerable ) , nameof ( Enumerable . Any ) , new Type [ ] { collectionItemType } ,
GetNestedPropertyExpression ( parameter , filter . Property ) , Expression . Lambda ( secondExpression , collectionItemTypeParameter ) ) ;
}
if ( primaryExpression ! = null & & secondExpression ! = null )
{
return filter . LogicalFilterOperator switch
{
LogicalFilterOperator . And = > Expression . AndAlso ( primaryExpression , secondExpression ) ,
LogicalFilterOperator . Or = > Expression . OrElse ( primaryExpression , secondExpression ) ,
_ = > primaryExpression
} ;
}
return primaryExpression ;
}
2021-10-07 11:35:29 +03:00
/// <summary>
/// The linq filter operators
/// </summary>
2021-05-25 09:01:11 +03:00
internal static readonly IDictionary < FilterOperator , string > LinqFilterOperators = new Dictionary < FilterOperator , string >
2021-05-17 04:23:29 -07:00
{
2025-02-13 13:35:41 +02:00
{ FilterOperator . Equals , "==" } ,
2021-05-17 04:23:29 -07:00
{ FilterOperator . NotEquals , "!=" } ,
{ FilterOperator . LessThan , "<" } ,
{ FilterOperator . LessThanOrEquals , "<=" } ,
{ FilterOperator . GreaterThan , ">" } ,
{ FilterOperator . GreaterThanOrEquals , ">=" } ,
{ FilterOperator . StartsWith , "StartsWith" } ,
{ FilterOperator . EndsWith , "EndsWith" } ,
2021-07-20 11:08:32 +03:00
{ FilterOperator . Contains , "Contains" } ,
2021-11-30 11:31:52 +02:00
{ FilterOperator . DoesNotContain , "DoesNotContain" } ,
2023-05-22 16:57:04 +03:00
{ FilterOperator . In , "In" } ,
{ FilterOperator . NotIn , "NotIn" } ,
2021-11-30 11:31:52 +02:00
{ FilterOperator . IsNull , "==" } ,
2022-07-07 14:14:30 +02:00
{ FilterOperator . IsEmpty , "==" } ,
{ FilterOperator . IsNotNull , "!=" } ,
2023-09-28 12:56:21 +07:00
{ FilterOperator . IsNotEmpty , "!=" } ,
{ FilterOperator . Custom , "" }
2021-05-17 04:23:29 -07:00
} ;
2021-10-07 11:35:29 +03:00
/// <summary>
/// The o data filter operators
/// </summary>
2021-05-25 09:01:11 +03:00
internal static readonly IDictionary < FilterOperator , string > ODataFilterOperators = new Dictionary < FilterOperator , string >
2021-05-18 11:39:53 +03:00
{
{ FilterOperator . Equals , "eq" } ,
{ FilterOperator . NotEquals , "ne" } ,
{ FilterOperator . LessThan , "lt" } ,
{ FilterOperator . LessThanOrEquals , "le" } ,
{ FilterOperator . GreaterThan , "gt" } ,
{ FilterOperator . GreaterThanOrEquals , "ge" } ,
{ FilterOperator . StartsWith , "startswith" } ,
{ FilterOperator . EndsWith , "endswith" } ,
2021-07-20 11:08:32 +03:00
{ FilterOperator . Contains , "contains" } ,
2025-05-20 11:46:24 +03:00
{ FilterOperator . DoesNotContain , "contains" } ,
2021-11-30 11:31:52 +02:00
{ FilterOperator . IsNull , "eq" } ,
2022-07-07 14:14:30 +02:00
{ FilterOperator . IsEmpty , "eq" } ,
{ FilterOperator . IsNotNull , "ne" } ,
2023-05-23 14:33:57 +03:00
{ FilterOperator . IsNotEmpty , "ne" } ,
{ FilterOperator . In , "in" } ,
2023-09-28 12:56:21 +07:00
{ FilterOperator . NotIn , "in" } ,
{ FilterOperator . Custom , "" }
2021-05-18 11:39:53 +03:00
} ;
2021-10-07 11:35:29 +03:00
/// <summary>
/// Converts to list.
/// </summary>
/// <param name="query">The query.</param>
/// <returns>IList.</returns>
2021-01-19 11:02:49 +02:00
public static IList ToList ( IQueryable query )
{
var genericToList = typeof ( Enumerable ) . GetMethod ( "ToList" )
. MakeGenericMethod ( new Type [ ] { query . ElementType } ) ;
return ( IList ) genericToList . Invoke ( null , new [ ] { query } ) ;
}
2021-10-07 11:35:29 +03:00
/// <summary>
/// Converts to filterstring.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="columns">The columns.</param>
/// <returns>System.String.</returns>
2021-05-17 04:23:29 -07:00
public static string ToFilterString < T > ( this IEnumerable < RadzenDataGridColumn < T > > columns )
{
2025-05-07 13:11:30 +03:00
Func < RadzenDataGridColumn < T > , bool > canFilter = ( c ) = > c . Filterable & & c . FilterPropertyType ! = null & &
( ! ( c . GetFilterValue ( ) = = null | | c . GetFilterValue ( ) as string = = string . Empty )
| | c . GetFilterOperator ( ) = = FilterOperator . IsNotNull | | c . GetFilterOperator ( ) = = FilterOperator . IsNull
| | c . GetFilterOperator ( ) = = FilterOperator . IsEmpty | | c . GetFilterOperator ( ) = = FilterOperator . IsNotEmpty )
& & c . GetFilterProperty ( ) ! = null ;
2024-02-26 08:00:34 +01:00
2025-05-07 13:11:30 +03:00
Func < RadzenDataGridColumn < T > , bool > canFilterCustom = ( c ) = > c . Filterable & & c . FilterPropertyType ! = null & &
( c . GetFilterOperator ( ) = = FilterOperator . Custom & & c . GetCustomFilterExpression ( ) ! = null )
& & c . GetFilterProperty ( ) ! = null ;
2021-05-17 04:23:29 -07:00
2025-05-07 13:11:30 +03:00
var columnsToFilter = columns . Where ( canFilter ) ;
var grid = columns . FirstOrDefault ( ) ? . Grid ;
var gridLogicalFilterOperator = grid ! = null ? grid . LogicalFilterOperator : LogicalFilterOperator . And ;
var gridFilterCaseSensitivity = grid ! = null ? grid . FilterCaseSensitivity : FilterCaseSensitivity . Default ;
2025-02-13 13:35:41 +02:00
2025-05-07 13:11:30 +03:00
var serializer = new ExpressionSerializer ( ) ;
var filterExpression = "" ;
if ( columnsToFilter . Any ( ) )
{
var filters = columnsToFilter . Select ( c = > new FilterDescriptor ( )
2021-05-17 04:23:29 -07:00
{
2025-05-07 13:11:30 +03:00
Property = c . Property ,
FilterProperty = c . FilterProperty ,
Type = c . FilterPropertyType ,
FilterValue = c . GetFilterValue ( ) ,
FilterOperator = c . GetFilterOperator ( ) ,
SecondFilterValue = c . GetSecondFilterValue ( ) ,
SecondFilterOperator = c . GetSecondFilterOperator ( ) ,
LogicalFilterOperator = c . GetLogicalFilterOperator ( )
} ) ;
2021-06-30 16:09:43 +03:00
2025-05-07 13:11:30 +03:00
if ( filters . Any ( ) )
{
var parameter = Expression . Parameter ( typeof ( T ) , "x" ) ;
Expression combinedExpression = null ;
2021-11-18 16:02:26 +02:00
2025-05-07 13:11:30 +03:00
foreach ( var filter in filters )
2021-11-18 16:02:26 +02:00
{
2025-05-07 13:11:30 +03:00
var expression = GetExpression < T > ( parameter , filter , gridFilterCaseSensitivity , filter . Type ) ;
if ( expression = = null ) continue ;
2024-12-19 09:24:23 +02:00
2025-05-07 13:11:30 +03:00
combinedExpression = combinedExpression = = null
? expression
: gridLogicalFilterOperator = = LogicalFilterOperator . And ?
Expression . AndAlso ( combinedExpression , expression ) :
Expression . OrElse ( combinedExpression , expression ) ;
2025-02-18 10:38:52 +02:00
}
2025-05-07 13:11:30 +03:00
if ( combinedExpression ! = null )
2021-06-30 16:09:43 +03:00
{
2025-05-07 13:11:30 +03:00
filterExpression = serializer . Serialize ( Expression . Lambda < Func < T , bool > > ( combinedExpression , parameter ) ) ;
2021-06-30 16:09:43 +03:00
}
2025-05-07 13:11:30 +03:00
}
2021-05-17 04:23:29 -07:00
2025-05-07 13:11:30 +03:00
}
2021-05-17 04:23:29 -07:00
2025-05-07 13:11:30 +03:00
var columnsWithCustomFilter = columns . Where ( canFilterCustom ) ;
2021-05-17 04:23:29 -07:00
2025-05-07 13:11:30 +03:00
var customFilterExpression = "" ;
if ( columnsToFilter . Any ( ) )
{
var expressions = columnsWithCustomFilter . Select ( c = > ( c . GetCustomFilterExpression ( ) ? ? "" ) . Replace ( " or " , " || " ) . Replace ( " and " , " && " ) ) . Where ( e = > ! string . IsNullOrEmpty ( e ) ) . ToList ( ) ;
customFilterExpression = string . Join ( $"{(gridLogicalFilterOperator == LogicalFilterOperator.And ? " & & " : " | | ")}" , expressions ) ;
2021-05-17 04:23:29 -07:00
2025-05-07 13:11:30 +03:00
return ! string . IsNullOrEmpty ( filterExpression ) & & ! string . IsNullOrEmpty ( customFilterExpression ) ?
$"{filterExpression} {(gridLogicalFilterOperator == LogicalFilterOperator.And ? " & & " : " | | ")} {customFilterExpression}" :
! string . IsNullOrEmpty ( customFilterExpression ) ? "it => " + customFilterExpression : filterExpression ;
2021-05-17 04:23:29 -07:00
}
2025-05-07 13:11:30 +03:00
return filterExpression ;
2021-05-17 04:23:29 -07:00
}
2024-06-11 07:44:35 +02:00
/// <summary>
/// Converts a RadzenDataFilter to a Linq-compatibly filter string
/// </summary>
/// <typeparam name="T">The type that is being filtered</typeparam>
2024-09-17 09:51:11 +03:00
/// <param name="dataFilter">The RadzenDataFilter component</param>
2024-06-11 07:44:35 +02:00
/// <returns>A Linq-compatible filter string</returns>
2024-09-17 09:51:11 +03:00
public static string ToFilterString < T > ( this RadzenDataFilter < T > dataFilter )
2024-06-11 07:44:35 +02:00
{
2024-09-17 09:51:11 +03:00
Func < CompositeFilterDescriptor , bool > canFilter = ( c ) = > dataFilter . properties . Where ( col = > col . Property = = c . Property ) . FirstOrDefault ( ) ? . FilterPropertyType ! = null & &
2025-05-07 13:11:30 +03:00
( ! ( c . FilterValue = = null | | c . FilterValue as string = = string . Empty )
| | c . FilterOperator = = FilterOperator . IsNotNull | | c . FilterOperator = = FilterOperator . IsNull
| | c . FilterOperator = = FilterOperator . IsEmpty | | c . FilterOperator = = FilterOperator . IsNotEmpty )
& & c . Property ! = null ;
2024-09-17 09:51:11 +03:00
if ( dataFilter . Filters . Concat ( dataFilter . Filters . SelectManyRecursive ( i = > i . Filters ? ? Enumerable . Empty < CompositeFilterDescriptor > ( ) ) ) . Where ( canFilter ) . Any ( ) )
{
2025-05-07 13:11:30 +03:00
var serializer = new ExpressionSerializer ( ) ;
2024-06-11 07:44:35 +02:00
2025-05-07 13:11:30 +03:00
var filterExpressions = new List < Expression > ( ) ;
2024-06-11 07:44:35 +02:00
2025-05-07 13:11:30 +03:00
var parameter = Expression . Parameter ( typeof ( T ) , "x" ) ;
2024-06-11 07:44:35 +02:00
2025-05-07 13:11:30 +03:00
foreach ( var filter in dataFilter . Filters )
2024-06-11 07:44:35 +02:00
{
2025-05-07 13:11:30 +03:00
AddWhereExpression < T > ( parameter , filter , ref filterExpressions , dataFilter . FilterCaseSensitivity ) ;
2024-06-11 07:44:35 +02:00
}
2024-06-10 10:40:57 -04:00
2025-05-07 13:11:30 +03:00
Expression combinedExpression = null ;
2024-05-20 10:55:21 +03:00
2025-05-07 13:11:30 +03:00
foreach ( var expression in filterExpressions )
2022-07-07 14:14:30 +02:00
{
2025-05-07 13:11:30 +03:00
combinedExpression = combinedExpression = = null
? expression
: dataFilter . LogicalFilterOperator = = LogicalFilterOperator . And ?
Expression . AndAlso ( combinedExpression , expression ) :
Expression . OrElse ( combinedExpression , expression ) ;
2022-07-07 14:14:30 +02:00
}
2021-05-17 04:23:29 -07:00
2025-05-07 13:11:30 +03:00
if ( combinedExpression ! = null )
2021-11-30 11:31:52 +02:00
{
2025-05-07 13:11:30 +03:00
return serializer . Serialize ( Expression . Lambda < Func < T , bool > > ( combinedExpression , parameter ) ) ;
2021-11-30 11:31:52 +02:00
}
2021-05-17 04:23:29 -07:00
}
return "" ;
}
2025-05-20 11:46:24 +03:00
/// <summary>
/// Converts a enumerable of CompositeFilterDescriptor to a Linq-compatibly filter string
/// </summary>
/// <typeparam name="T">The type that is being filtered</typeparam>
/// <param name="filters">The enumerable of CompositeFilterDescriptor </param>
/// <param name="logicalFilterOperator">The LogicalFilterOperator</param>
/// <param name="filterCaseSensitivity">The FilterCaseSensitivity</param>
/// <returns>A Linq-compatible filter string</returns>
public static string ToFilterString < T > ( this IEnumerable < CompositeFilterDescriptor > filters ,
LogicalFilterOperator logicalFilterOperator = LogicalFilterOperator . And ,
FilterCaseSensitivity filterCaseSensitivity = FilterCaseSensitivity . Default )
{
Func < CompositeFilterDescriptor , bool > canFilter = ( c ) = >
( ! ( c . FilterValue = = null | | c . FilterValue as string = = string . Empty )
| | c . FilterOperator = = FilterOperator . IsNotNull | | c . FilterOperator = = FilterOperator . IsNull
| | c . FilterOperator = = FilterOperator . IsEmpty | | c . FilterOperator = = FilterOperator . IsNotEmpty )
& & c . Property ! = null ;
if ( filters . Concat ( filters . SelectManyRecursive ( i = > i . Filters ? ? Enumerable . Empty < CompositeFilterDescriptor > ( ) ) ) . Where ( canFilter ) . Any ( ) )
{
var serializer = new ExpressionSerializer ( ) ;
var filterExpressions = new List < Expression > ( ) ;
var parameter = Expression . Parameter ( typeof ( T ) , "x" ) ;
foreach ( var filter in filters )
{
AddWhereExpression < T > ( parameter , filter , ref filterExpressions , filterCaseSensitivity ) ;
}
Expression combinedExpression = null ;
foreach ( var expression in filterExpressions )
{
combinedExpression = combinedExpression = = null
? expression
: logicalFilterOperator = = LogicalFilterOperator . And ?
Expression . AndAlso ( combinedExpression , expression ) :
Expression . OrElse ( combinedExpression , expression ) ;
}
if ( combinedExpression ! = null )
{
return serializer . Serialize ( Expression . Lambda < Func < T , bool > > ( combinedExpression , parameter ) ) ;
}
}
return "" ;
}
2021-10-07 11:35:29 +03:00
/// <summary>
/// Gets the column o data filter.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="column">The column.</param>
2023-04-11 00:50:19 -05:00
/// <param name="filterValue">The specific value to filter by</param>
/// <param name="columnFilterOperator">The operator used to compare to <paramref name="filterValue"/></param>
2021-10-07 11:35:29 +03:00
/// <returns>System.String.</returns>
2023-04-11 00:50:19 -05:00
internal static string GetColumnODataFilter < T > ( RadzenDataGridColumn < T > column , object filterValue , FilterOperator columnFilterOperator )
2021-05-17 04:23:29 -07:00
{
var property = column . GetFilterProperty ( ) . Replace ( '.' , '/' ) ;
2021-11-30 11:31:52 +02:00
var odataFilterOperator = ODataFilterOperators [ columnFilterOperator ] ;
2021-05-17 04:23:29 -07:00
2023-04-11 00:50:19 -05:00
var value = IsEnumerable ( column . FilterPropertyType ) & & column . FilterPropertyType ! = typeof ( string )
? null
2024-09-04 09:02:39 +03:00
: ( string ) Convert . ChangeType ( filterValue is DateTimeOffset ?
2024-11-05 10:52:32 +02:00
( ( DateTimeOffset ) filterValue ) . UtcDateTime : filterValue is DateOnly ?
2025-05-07 13:11:30 +03:00
( ( DateOnly ) filterValue ) . ToString ( "yyy-MM-dd" , CultureInfo . InvariantCulture ) :
2024-11-05 10:52:32 +02:00
filterValue , typeof ( string ) , CultureInfo . InvariantCulture ) ;
2021-05-17 04:23:29 -07:00
2021-05-18 10:04:34 +03:00
if ( column . Grid . FilterCaseSensitivity = = FilterCaseSensitivity . CaseInsensitive & & column . FilterPropertyType = = typeof ( string ) )
2021-05-17 04:23:29 -07:00
{
property = $"tolower({property})" ;
}
2023-03-14 09:33:47 +02:00
if ( PropertyAccess . IsEnum ( column . FilterPropertyType ) | | PropertyAccess . IsNullableEnum ( column . FilterPropertyType ) )
{
return $"{property} {odataFilterOperator} '{value}'" ;
}
else if ( column . FilterPropertyType = = typeof ( string ) )
2021-05-17 04:23:29 -07:00
{
2021-05-18 10:04:34 +03:00
if ( ! string . IsNullOrEmpty ( value ) & & columnFilterOperator = = FilterOperator . Contains )
2021-05-17 04:23:29 -07:00
{
return column . Grid . FilterCaseSensitivity = = FilterCaseSensitivity . CaseInsensitive ?
$"contains({property}, tolower('{value}'))" :
$"contains({property}, '{value}')" ;
}
2021-07-20 11:08:32 +03:00
else if ( ! string . IsNullOrEmpty ( value ) & & columnFilterOperator = = FilterOperator . DoesNotContain )
{
return column . Grid . FilterCaseSensitivity = = FilterCaseSensitivity . CaseInsensitive ?
$"not(contains({property}, tolower('{value}')))" :
$"not(contains({property}, '{value}'))" ;
}
2021-05-17 04:23:29 -07:00
else if ( ! string . IsNullOrEmpty ( value ) & & columnFilterOperator = = FilterOperator . StartsWith )
{
return column . Grid . FilterCaseSensitivity = = FilterCaseSensitivity . CaseInsensitive ?
$"startswith({property}, tolower('{value}'))" :
$"startswith({property}, '{value}')" ;
}
else if ( ! string . IsNullOrEmpty ( value ) & & columnFilterOperator = = FilterOperator . EndsWith )
{
return column . Grid . FilterCaseSensitivity = = FilterCaseSensitivity . CaseInsensitive ?
$"endswith({property}, tolower('{value}'))" :
$"endswith({property}, '{value}')" ;
}
else if ( ! string . IsNullOrEmpty ( value ) & & columnFilterOperator = = FilterOperator . Equals )
{
2021-05-31 09:58:46 +03:00
return column . Grid . FilterCaseSensitivity = = FilterCaseSensitivity . CaseInsensitive ?
$"{property} eq tolower('{value}')" :
$"{property} eq '{value}'" ;
}
else if ( ! string . IsNullOrEmpty ( value ) & & columnFilterOperator = = FilterOperator . NotEquals )
{
return column . Grid . FilterCaseSensitivity = = FilterCaseSensitivity . CaseInsensitive ?
$"{property} ne tolower('{value}')" :
$"{property} ne '{value}'" ;
2021-05-17 04:23:29 -07:00
}
2021-11-30 11:31:52 +02:00
else if ( columnFilterOperator = = FilterOperator . IsNull | | columnFilterOperator = = FilterOperator . IsNotNull )
{
return $"{property} {odataFilterOperator} null" ;
}
2022-07-07 14:14:30 +02:00
else if ( columnFilterOperator = = FilterOperator . IsEmpty | | columnFilterOperator = = FilterOperator . IsNotEmpty )
{
return $"{property} {odataFilterOperator} ''" ;
}
2021-11-18 16:02:26 +02:00
}
2022-07-06 14:09:26 +03:00
else if ( IsEnumerable ( column . FilterPropertyType ) & & column . FilterPropertyType ! = typeof ( string ) )
2021-11-18 16:02:26 +02:00
{
2023-04-11 00:50:19 -05:00
var enumerableValue = ( ( IEnumerable ) ( filterValue ! = null ? filterValue : Enumerable . Empty < object > ( ) ) ) . AsQueryable ( ) ;
2021-11-19 12:36:34 +02:00
var enumerableValueAsString = "(" + String . Join ( "," ,
( enumerableValue . ElementType = = typeof ( string ) ? enumerableValue . Cast < string > ( ) . Select ( i = > $@"'{i}'" ) . Cast < object > ( ) : enumerableValue . Cast < object > ( ) ) ) + ")" ;
2023-05-23 14:33:57 +03:00
var enumerableValueAsStringOrForAny = String . Join ( " or " ,
( enumerableValue . ElementType = = typeof ( string ) ? enumerableValue . Cast < string > ( )
. Select ( i = > $@"i/{property} eq '{i}'" ) . Cast < object > ( ) : enumerableValue . Cast < object > ( ) . Select ( i = > $@"i/{property} eq {i}" ) . Cast < object > ( ) ) ) ;
2025-02-13 13:35:41 +02:00
if ( enumerableValue . Cast < object > ( ) . Any ( ) & & columnFilterOperator = = FilterOperator . Contains )
2021-11-19 12:36:34 +02:00
{
return $"{property} in {enumerableValueAsString}" ;
}
2025-02-13 13:35:41 +02:00
else if ( enumerableValue . Cast < object > ( ) . Any ( ) & & columnFilterOperator = = FilterOperator . DoesNotContain )
2021-11-19 12:36:34 +02:00
{
return $"not({property} in {enumerableValueAsString})" ;
}
2025-02-13 13:35:41 +02:00
else if ( enumerableValue . Cast < object > ( ) . Any ( ) & & columnFilterOperator = = FilterOperator . In )
2023-05-23 14:33:57 +03:00
{
return $"{column.Property}/any(i:{enumerableValueAsStringOrForAny})" ;
}
2025-02-13 13:35:41 +02:00
else if ( enumerableValue . Cast < object > ( ) . Any ( ) & & columnFilterOperator = = FilterOperator . NotIn )
2023-05-23 14:33:57 +03:00
{
return $"not({column.Property}/any(i: {enumerableValueAsStringOrForAny}))" ;
}
2021-05-17 04:23:29 -07:00
}
else if ( PropertyAccess . IsNumeric ( column . FilterPropertyType ) )
{
2024-06-11 01:45:34 -04:00
if ( columnFilterOperator = = FilterOperator . IsNull | | columnFilterOperator = = FilterOperator . IsNotNull )
{
return $"{property} {odataFilterOperator} null" ;
}
else
{
return $"{property} {odataFilterOperator} {value}" ;
}
2021-05-17 04:23:29 -07:00
}
else if ( column . FilterPropertyType = = typeof ( bool ) | | column . FilterPropertyType = = typeof ( bool? ) )
{
2021-11-30 11:31:52 +02:00
if ( columnFilterOperator = = FilterOperator . IsNull | | columnFilterOperator = = FilterOperator . IsNotNull )
{
return $"{property} {odataFilterOperator} null" ;
}
2022-07-07 14:14:30 +02:00
else if ( columnFilterOperator = = FilterOperator . IsEmpty | | columnFilterOperator = = FilterOperator . IsNotEmpty )
{
return $"{property} {odataFilterOperator} ''" ;
}
2021-11-30 11:31:52 +02:00
else
{
return $"{property} eq {value.ToLower()}" ;
}
2021-05-17 04:23:29 -07:00
}
else if ( column . FilterPropertyType = = typeof ( DateTime ) | |
column . FilterPropertyType = = typeof ( DateTime ? ) | |
column . FilterPropertyType = = typeof ( DateTimeOffset ) | |
2024-11-21 07:52:24 +01:00
column . FilterPropertyType = = typeof ( DateTimeOffset ? ) | |
2025-05-07 13:11:30 +03:00
column . FilterPropertyType = = typeof ( DateOnly ) | |
2024-11-21 07:52:24 +01:00
column . FilterPropertyType = = typeof ( DateOnly ? ) )
2021-05-17 04:23:29 -07:00
{
2021-11-30 11:31:52 +02:00
if ( columnFilterOperator = = FilterOperator . IsNull | | columnFilterOperator = = FilterOperator . IsNotNull )
{
return $"{property} {odataFilterOperator} null" ;
}
2022-07-07 14:14:30 +02:00
else if ( columnFilterOperator = = FilterOperator . IsEmpty | | columnFilterOperator = = FilterOperator . IsNotEmpty )
{
return $"{property} {odataFilterOperator} ''" ;
}
2021-11-30 11:31:52 +02:00
else
{
2024-12-02 11:28:53 +02:00
return $"{property} {odataFilterOperator} {(column.FilterPropertyType == typeof(DateOnly) || column.FilterPropertyType == typeof(DateOnly?) ? value : DateTime.Parse(value, CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.RoundtripKind).ToString(" yyyy - MM - ddTHH : mm : ss . fffZ ", CultureInfo.InvariantCulture))}" ;
2021-11-30 11:31:52 +02:00
}
2021-05-17 04:23:29 -07:00
}
else if ( column . FilterPropertyType = = typeof ( Guid ) | | column . FilterPropertyType = = typeof ( Guid ? ) )
{
2021-11-30 11:31:52 +02:00
return $"{property} {odataFilterOperator} {value}" ;
2021-05-17 04:23:29 -07:00
}
return "" ;
}
2021-10-07 11:35:29 +03:00
/// <summary>
/// Converts to odatafilterstring.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="columns">The columns.</param>
/// <returns>System.String.</returns>
2021-05-17 04:23:29 -07:00
public static string ToODataFilterString < T > ( this IEnumerable < RadzenDataGridColumn < T > > columns )
{
2024-02-29 09:53:57 +01:00
var columnsWithFilter = GetFilterableColumns ( columns ) ;
2021-05-17 04:23:29 -07:00
2024-02-29 09:53:57 +01:00
if ( columnsWithFilter . Any ( ) )
2021-05-17 04:23:29 -07:00
{
var gridLogicalFilterOperator = columns . FirstOrDefault ( ) ? . Grid ? . LogicalFilterOperator ;
var gridBooleanOperator = gridLogicalFilterOperator = = LogicalFilterOperator . And ? "and" : "or" ;
var whereList = new List < string > ( ) ;
2024-02-29 09:53:57 +01:00
foreach ( var column in columnsWithFilter )
2021-05-17 04:23:29 -07:00
{
var property = column . GetFilterProperty ( ) . Replace ( '.' , '/' ) ;
2021-11-19 12:36:34 +02:00
var value = column . GetFilterValue ( ) ;
var secondValue = column . GetSecondFilterValue ( ) ;
2021-05-17 04:23:29 -07:00
2024-02-06 13:20:46 +00:00
if ( column . GetFilterOperator ( ) = = FilterOperator . Custom )
{
var customFilterExpression = column . GetCustomFilterExpression ( ) ;
if ( ! string . IsNullOrEmpty ( customFilterExpression ) )
{
whereList . Add ( customFilterExpression ) ;
}
}
else if ( value ! = null | | column . GetFilterOperator ( ) = = FilterOperator . IsNotNull | | column . GetFilterOperator ( ) = = FilterOperator . IsNull
2022-07-07 14:14:30 +02:00
| | column . GetFilterOperator ( ) = = FilterOperator . IsEmpty | | column . GetFilterOperator ( ) = = FilterOperator . IsNotEmpty )
2021-05-17 04:23:29 -07:00
{
2021-05-18 11:39:53 +03:00
var linqOperator = ODataFilterOperators [ column . GetFilterOperator ( ) ] ;
2021-05-17 04:23:29 -07:00
if ( linqOperator = = null )
{
linqOperator = "==" ;
}
var booleanOperator = column . LogicalFilterOperator = = LogicalFilterOperator . And ? "and" : "or" ;
2021-11-19 12:36:34 +02:00
if ( secondValue = = null )
2021-05-17 04:23:29 -07:00
{
2023-04-11 00:50:19 -05:00
whereList . Add ( column . GetColumnODataFilter ( ) ) ;
2021-05-17 04:23:29 -07:00
}
else
2023-04-11 00:50:19 -05:00
{
whereList . Add ( $"({column.GetColumnODataFilter()} {booleanOperator} {column.GetColumnODataFilter(second: true)})" ) ;
2021-05-17 04:23:29 -07:00
}
}
}
return string . Join ( $" {gridBooleanOperator} " , whereList . Where ( i = > ! string . IsNullOrEmpty ( i ) ) ) ;
}
return "" ;
}
2023-06-13 11:32:54 +03:00
/// <summary>
/// Gets if type is IEnumerable.
/// </summary>
public static bool IsEnumerable ( Type type )
2022-07-06 14:09:26 +03:00
{
2025-05-14 09:18:30 +03:00
return ( typeof ( IEnumerable ) . IsAssignableFrom ( type ) | | typeof ( IEnumerable < > ) . IsAssignableFrom ( type ) ) & & type ! = typeof ( string ) ;
2022-07-06 14:09:26 +03:00
}
2021-10-07 11:35:29 +03:00
/// <summary>
/// Wheres the specified columns.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source">The source.</param>
/// <param name="columns">The columns.</param>
/// <returns>IQueryable<T>.</returns>
2021-05-17 04:23:29 -07:00
public static IQueryable < T > Where < T > ( this IQueryable < T > source , IEnumerable < RadzenDataGridColumn < T > > columns )
{
Func < RadzenDataGridColumn < T > , bool > canFilter = ( c ) = > c . Filterable & & c . FilterPropertyType ! = null & &
2023-04-11 00:50:19 -05:00
( ! ( c . GetFilterValue ( ) = = null | | c . GetFilterValue ( ) as string = = string . Empty )
2022-07-07 14:14:30 +02:00
| | c . GetFilterOperator ( ) = = FilterOperator . IsNotNull | | c . GetFilterOperator ( ) = = FilterOperator . IsNull
2025-02-14 14:38:12 +02:00
| | c . GetFilterOperator ( ) = = FilterOperator . IsEmpty | | c . GetFilterOperator ( ) = = FilterOperator . IsNotEmpty )
& & c . GetFilterProperty ( ) ! = null ;
Func < RadzenDataGridColumn < T > , bool > canFilterCustom = ( c ) = > c . Filterable & & c . FilterPropertyType ! = null & &
( c . GetFilterOperator ( ) = = FilterOperator . Custom & & c . GetCustomFilterExpression ( ) ! = null )
2022-07-07 14:14:30 +02:00
& & c . GetFilterProperty ( ) ! = null ;
2021-05-17 04:23:29 -07:00
2025-02-13 13:35:41 +02:00
var columnsToFilter = columns . Where ( canFilter ) ;
var grid = columns . FirstOrDefault ( ) ? . Grid ;
2025-02-13 14:53:45 +02:00
var gridLogicalFilterOperator = grid ! = null ? grid . LogicalFilterOperator : LogicalFilterOperator . And ;
var gridFilterCaseSensitivity = grid ! = null ? grid . FilterCaseSensitivity : FilterCaseSensitivity . Default ;
2023-04-11 00:50:19 -05:00
2025-02-13 13:35:41 +02:00
if ( columnsToFilter . Any ( ) )
{
2025-02-14 14:38:12 +02:00
source = source . Where ( columnsToFilter . Select ( c = > new FilterDescriptor ( )
2025-05-07 13:11:30 +03:00
{
Property = c . Property ,
FilterProperty = c . FilterProperty ,
Type = c . FilterPropertyType ,
FilterValue = c . GetFilterValue ( ) ,
FilterOperator = c . GetFilterOperator ( ) ,
SecondFilterValue = c . GetSecondFilterValue ( ) ,
SecondFilterOperator = c . GetSecondFilterOperator ( ) ,
LogicalFilterOperator = c . GetLogicalFilterOperator ( )
} ) , gridLogicalFilterOperator , gridFilterCaseSensitivity ) ;
2021-05-17 04:23:29 -07:00
}
2025-02-14 14:38:12 +02:00
var columnsWithCustomFilter = columns . Where ( canFilterCustom ) ;
if ( columnsToFilter . Any ( ) )
{
var expressions = columnsWithCustomFilter . Select ( c = > ( c . GetCustomFilterExpression ( ) ? ? "" ) . Replace ( " or " , " || " ) . Replace ( " and " , " && " ) ) . Where ( e = > ! string . IsNullOrEmpty ( e ) ) . ToList ( ) ;
2025-05-07 13:11:30 +03:00
source = expressions . Any ( ) ?
2025-02-14 14:38:12 +02:00
System . Linq . Dynamic . Core . DynamicExtensions . Where ( source , "it => " + string . Join ( $"{(gridLogicalFilterOperator == LogicalFilterOperator.And ? " & & " : " | | ")}" , expressions ) ) : source ;
}
2021-05-17 04:23:29 -07:00
return source ;
}
2022-09-20 14:59:15 +03:00
/// <summary>
/// Wheres the specified filters.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source">The source.</param>
/// <param name="dataFilter">The DataFilter.</param>
/// <returns>IQueryable<T>.</returns>
public static IQueryable < T > Where < T > ( this IQueryable < T > source , RadzenDataFilter < T > dataFilter )
{
Func < CompositeFilterDescriptor , bool > canFilter = ( c ) = > dataFilter . properties . Where ( col = > col . Property = = c . Property ) . FirstOrDefault ( ) ? . FilterPropertyType ! = null & &
( ! ( c . FilterValue = = null | | c . FilterValue as string = = string . Empty )
| | c . FilterOperator = = FilterOperator . IsNotNull | | c . FilterOperator = = FilterOperator . IsNull
| | c . FilterOperator = = FilterOperator . IsEmpty | | c . FilterOperator = = FilterOperator . IsNotEmpty )
& & c . Property ! = null ;
2023-02-15 18:16:35 +02:00
if ( dataFilter . Filters . Concat ( dataFilter . Filters . SelectManyRecursive ( i = > i . Filters ? ? Enumerable . Empty < CompositeFilterDescriptor > ( ) ) ) . Where ( canFilter ) . Any ( ) )
2022-09-20 14:59:15 +03:00
{
2025-02-13 13:35:41 +02:00
var filterExpressions = new List < Expression > ( ) ;
var parameter = Expression . Parameter ( typeof ( T ) , "x" ) ;
2022-09-20 18:23:15 +03:00
foreach ( var filter in dataFilter . Filters )
2022-09-20 14:59:15 +03:00
{
2025-02-13 13:35:41 +02:00
AddWhereExpression < T > ( parameter , filter , ref filterExpressions , dataFilter . FilterCaseSensitivity ) ;
2022-09-20 14:59:15 +03:00
}
2025-02-13 13:35:41 +02:00
Expression combinedExpression = null ;
foreach ( var expression in filterExpressions )
{
combinedExpression = combinedExpression = = null
? expression
: dataFilter . LogicalFilterOperator = = LogicalFilterOperator . And ?
Expression . AndAlso ( combinedExpression , expression ) :
Expression . OrElse ( combinedExpression , expression ) ;
}
if ( combinedExpression ! = null )
{
var lambda = Expression . Lambda < Func < T , bool > > ( combinedExpression , parameter ) ;
return source . Where ( lambda ) ;
}
2022-09-20 14:59:15 +03:00
}
return source ;
}
2025-02-13 13:35:41 +02:00
/// <summary>
/// Wheres the specified filters.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns>IQueryable<T>.</returns>
public static IQueryable < T > Where < T > ( this IQueryable < T > source , IEnumerable < CompositeFilterDescriptor > filters , LogicalFilterOperator logicalFilterOperator , FilterCaseSensitivity filterCaseSensitivity )
2022-09-20 14:59:15 +03:00
{
2025-05-07 13:11:30 +03:00
Func < CompositeFilterDescriptor , bool > canFilter = ( c ) = >
2025-02-13 13:35:41 +02:00
( ! ( c . FilterValue = = null | | c . FilterValue as string = = string . Empty )
| | c . FilterOperator = = FilterOperator . IsNotNull | | c . FilterOperator = = FilterOperator . IsNull
| | c . FilterOperator = = FilterOperator . IsEmpty | | c . FilterOperator = = FilterOperator . IsNotEmpty )
& & c . Property ! = null ;
if ( filters . Where ( canFilter ) . Any ( ) )
2022-09-20 14:59:15 +03:00
{
2025-02-13 13:35:41 +02:00
var filterExpressions = new List < Expression > ( ) ;
2022-09-20 18:23:15 +03:00
2025-02-13 13:35:41 +02:00
var parameter = Expression . Parameter ( typeof ( T ) , "x" ) ;
2022-09-20 18:23:15 +03:00
2025-02-13 13:35:41 +02:00
foreach ( var filter in filters )
2022-11-24 15:26:14 +02:00
{
2025-02-13 13:35:41 +02:00
AddWhereExpression < T > ( parameter , filter , ref filterExpressions , filterCaseSensitivity ) ;
2022-11-24 15:26:14 +02:00
}
2025-02-13 13:35:41 +02:00
Expression combinedExpression = null ;
2022-09-20 14:59:15 +03:00
2025-02-13 13:35:41 +02:00
foreach ( var expression in filterExpressions )
2022-09-20 14:59:15 +03:00
{
2025-02-13 13:35:41 +02:00
combinedExpression = combinedExpression = = null
? expression
: logicalFilterOperator = = LogicalFilterOperator . And ?
Expression . AndAlso ( combinedExpression , expression ) :
Expression . OrElse ( combinedExpression , expression ) ;
2022-09-20 14:59:15 +03:00
}
2025-02-13 13:35:41 +02:00
if ( combinedExpression ! = null )
2022-09-20 14:59:15 +03:00
{
2025-02-13 13:35:41 +02:00
var lambda = Expression . Lambda < Func < T , bool > > ( combinedExpression , parameter ) ;
return source . Where ( lambda ) ;
2022-09-20 14:59:15 +03:00
}
2025-02-13 13:35:41 +02:00
}
2022-09-20 14:59:15 +03:00
2025-02-13 13:35:41 +02:00
return source ;
}
2022-09-20 14:59:15 +03:00
2025-02-13 13:35:41 +02:00
private static void AddWhereExpression < T > ( ParameterExpression parameter , CompositeFilterDescriptor filter , ref List < Expression > filterExpressions , FilterCaseSensitivity filterCaseSensitivity )
{
if ( filter . Filters ! = null )
{
var innerFilterExpressions = new List < Expression > ( ) ;
2022-09-20 14:59:15 +03:00
2025-02-13 13:35:41 +02:00
foreach ( var f in filter . Filters )
2022-09-20 14:59:15 +03:00
{
2025-02-13 13:35:41 +02:00
AddWhereExpression < T > ( parameter , f , ref innerFilterExpressions , filterCaseSensitivity ) ;
2022-09-20 14:59:15 +03:00
}
2025-02-13 13:35:41 +02:00
if ( innerFilterExpressions . Any ( ) )
2022-09-20 14:59:15 +03:00
{
2025-02-13 13:35:41 +02:00
Expression combinedExpression = null ;
2022-09-26 21:47:24 +03:00
2025-02-13 13:35:41 +02:00
foreach ( var expression in innerFilterExpressions )
2022-09-20 14:59:15 +03:00
{
2025-02-13 13:35:41 +02:00
combinedExpression = combinedExpression = = null
? expression
: filter . LogicalFilterOperator = = LogicalFilterOperator . And ?
Expression . AndAlso ( combinedExpression , expression ) :
Expression . OrElse ( combinedExpression , expression ) ;
2022-09-20 14:59:15 +03:00
}
2025-02-13 13:35:41 +02:00
if ( combinedExpression ! = null )
2024-11-18 10:28:53 +02:00
{
2025-02-13 13:35:41 +02:00
filterExpressions . Add ( combinedExpression ) ;
2024-11-18 10:28:53 +02:00
}
}
2025-02-13 13:35:41 +02:00
}
else
{
if ( filter . Property = = null | | filter . FilterOperator = = null | | ( filter . FilterValue = = null & &
filter . FilterOperator ! = FilterOperator . IsNull & & filter . FilterOperator ! = FilterOperator . IsNotNull & &
filter . FilterOperator ! = FilterOperator . IsEmpty & & filter . FilterOperator ! = FilterOperator . IsNotEmpty ) )
2024-11-18 10:28:53 +02:00
{
2025-02-13 13:35:41 +02:00
return ;
2024-01-05 15:18:22 +02:00
}
2023-02-22 15:53:11 +02:00
2025-02-13 13:35:41 +02:00
var f = new FilterDescriptor ( )
{
Property = filter . Property ,
FilterProperty = filter . FilterProperty ,
FilterValue = filter . FilterValue ,
FilterOperator = filter . FilterOperator ? ? FilterOperator . Equals ,
LogicalFilterOperator = filter . LogicalFilterOperator ,
Type = filter . Type
} ;
2023-02-22 15:53:11 +02:00
2025-02-13 13:35:41 +02:00
var expression = GetExpression < T > ( parameter , f , filterCaseSensitivity , f . Type ) ;
if ( expression ! = null )
{
filterExpressions . Add ( expression ) ;
2022-09-20 14:59:15 +03:00
}
}
}
2024-07-18 13:32:08 +03:00
/// <summary>
/// Wheres the specified filters.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="property">The property.</param>
/// <param name="value">The value.</param>
/// <param name="op">The StringFilterOperator.</param>
/// <param name="cs">The FilterCaseSensitivity.</param>
/// <returns>IQueryable<T>.</returns>
public static IQueryable Where ( this IQueryable source , string property , string value , StringFilterOperator op , FilterCaseSensitivity cs )
{
2025-03-01 12:18:35 +02:00
IQueryable result ;
if ( ! string . IsNullOrEmpty ( value ) )
2024-07-18 13:32:08 +03:00
{
2025-03-01 12:18:35 +02:00
var ignoreCase = cs = = FilterCaseSensitivity . CaseInsensitive ;
var parameter = Expression . Parameter ( source . ElementType , "it" ) ;
var inMemory = typeof ( EnumerableQuery ) . IsAssignableFrom ( source . GetType ( ) ) ;
Expression propertyExpression = parameter ;
if ( ! string . IsNullOrEmpty ( property ) )
{
propertyExpression = GetNestedPropertyExpression ( parameter , property ) ;
}
if ( string . IsNullOrEmpty ( property ) & & inMemory | |
propertyExpression ! = null & & propertyExpression . Type ! = typeof ( string ) )
{
propertyExpression = Expression . Call ( notNullCheck ( parameter ) , "ToString" , Type . EmptyTypes ) ;
}
if ( ignoreCase )
{
propertyExpression = Expression . Call ( notNullCheck ( propertyExpression ) , "ToLower" , Type . EmptyTypes ) ;
}
var constantExpression = Expression . Constant ( ignoreCase ? value . ToLower ( ) : value , typeof ( string ) ) ;
Expression comparisonExpression = null ;
switch ( op )
{
case StringFilterOperator . Contains :
comparisonExpression = Expression . Call ( notNullCheck ( propertyExpression ) , "Contains" , null , constantExpression ) ;
break ;
case StringFilterOperator . StartsWith :
comparisonExpression = Expression . Call ( notNullCheck ( propertyExpression ) , "StartsWith" , null , constantExpression ) ;
break ;
case StringFilterOperator . EndsWith :
comparisonExpression = Expression . Call ( notNullCheck ( propertyExpression ) , "EndsWith" , null , constantExpression ) ;
break ;
default :
comparisonExpression = Expression . Equal ( propertyExpression , constantExpression ) ;
break ;
}
var lambda = Expression . Lambda ( comparisonExpression , parameter ) ;
result = source . Provider . CreateQuery ( Expression . Call (
typeof ( Queryable ) ,
"Where" ,
new Type [ ] { source . ElementType } ,
source . Expression ,
lambda
) ) ;
}
else
{
result = source ;
2024-07-18 13:32:08 +03:00
}
2025-03-01 12:18:35 +02:00
return result ;
2024-07-18 13:32:08 +03:00
}
2023-02-01 14:23:06 +02:00
/// <summary>
/// Converts to OData filter expression.
/// </summary>
/// <param name="dataFilter">The DataFilter.</param>
/// <returns>System.String.</returns>
public static string ToODataFilterString < T > ( this RadzenDataFilter < T > dataFilter )
{
Func < CompositeFilterDescriptor , bool > canFilter = ( c ) = > dataFilter . properties . Where ( col = > col . Property = = c . Property ) . FirstOrDefault ( ) ? . FilterPropertyType ! = null & &
( ! ( c . FilterValue = = null | | c . FilterValue as string = = string . Empty )
| | c . FilterOperator = = FilterOperator . IsNotNull | | c . FilterOperator = = FilterOperator . IsNull
| | c . FilterOperator = = FilterOperator . IsEmpty | | c . FilterOperator = = FilterOperator . IsNotEmpty )
& & c . Property ! = null ;
2023-02-15 18:16:35 +02:00
if ( dataFilter . Filters . Concat ( dataFilter . Filters . SelectManyRecursive ( i = > i . Filters ? ? Enumerable . Empty < CompositeFilterDescriptor > ( ) ) ) . Where ( canFilter ) . Any ( ) )
2023-02-01 14:23:06 +02:00
{
var filterExpressions = new List < string > ( ) ;
foreach ( var filter in dataFilter . Filters )
{
2025-05-20 11:46:24 +03:00
var ft = dataFilter . properties . Where ( col = > col . Property = = filter . Property ) . FirstOrDefault ( ) ? . FilterPropertyType ;
if ( ft ! = null & & ft ! = filter . Type )
{
filter . Type = ft ;
}
AddODataExpression < T > ( canFilter , filter , ref filterExpressions , dataFilter . LogicalFilterOperator , dataFilter . FilterCaseSensitivity ) ;
2023-02-01 14:23:06 +02:00
}
return filterExpressions . Any ( ) ?
string . Join ( $" {dataFilter.LogicalFilterOperator.ToString().ToLower()} " , filterExpressions )
: "" ;
}
return "" ;
}
2025-05-20 11:46:24 +03:00
/// <summary>
/// Converts a enumerable of CompositeFilterDescriptor to a OData-compatibly filter string
/// </summary>
/// <param name="filters">The enumerable of CompositeFilterDescriptor </param>
/// <param name="logicalFilterOperator">The LogicalFilterOperator</param>
/// <param name="filterCaseSensitivity">The FilterCaseSensitivity</param>
/// <returns>A OData-compatible filter string</returns>
public static string ToODataFilterString < T > ( this IEnumerable < CompositeFilterDescriptor > filters ,
LogicalFilterOperator logicalFilterOperator = LogicalFilterOperator . And ,
FilterCaseSensitivity filterCaseSensitivity = FilterCaseSensitivity . Default )
{
Func < CompositeFilterDescriptor , bool > canFilter = ( c ) = >
( ! ( c . FilterValue = = null | | c . FilterValue as string = = string . Empty )
| | c . FilterOperator = = FilterOperator . IsNotNull | | c . FilterOperator = = FilterOperator . IsNull
| | c . FilterOperator = = FilterOperator . IsEmpty | | c . FilterOperator = = FilterOperator . IsNotEmpty )
& & c . Property ! = null ;
if ( filters . Concat ( filters . SelectManyRecursive ( i = > i . Filters ? ? Enumerable . Empty < CompositeFilterDescriptor > ( ) ) ) . Where ( canFilter ) . Any ( ) )
{
var filterExpressions = new List < string > ( ) ;
foreach ( var filter in filters )
{
AddODataExpression < T > ( canFilter , filter , ref filterExpressions , logicalFilterOperator , filterCaseSensitivity ) ;
}
return filterExpressions . Any ( ) ?
string . Join ( $" {logicalFilterOperator.ToString().ToLower()} " , filterExpressions )
: "" ;
}
return "" ;
}
private static void AddODataExpression < T > ( Func < CompositeFilterDescriptor , bool > canFilter ,
CompositeFilterDescriptor filter , ref List < string > filterExpressions ,
LogicalFilterOperator logicalFilterOperator = LogicalFilterOperator . And ,
FilterCaseSensitivity filterCaseSensitivity = FilterCaseSensitivity . Default )
2023-02-01 14:23:06 +02:00
{
if ( filter . Filters ! = null )
{
var innerFilterExpressions = new List < string > ( ) ;
foreach ( var f in filter . Filters )
{
2025-05-20 11:46:24 +03:00
AddODataExpression < T > ( canFilter , f , ref innerFilterExpressions , logicalFilterOperator , filterCaseSensitivity ) ;
2023-02-01 14:23:06 +02:00
}
if ( innerFilterExpressions . Any ( ) )
{
filterExpressions . Add ( "(" + string . Join ( $" {filter.LogicalFilterOperator.ToString().ToLower()} " , innerFilterExpressions ) + ")" ) ;
}
}
else
{
2024-02-07 02:32:04 -05:00
if ( filter . Property = = null | | filter . FilterOperator = = null | | ( filter . FilterValue = = null & &
2023-02-01 14:23:06 +02:00
filter . FilterOperator ! = FilterOperator . IsNull & & filter . FilterOperator ! = FilterOperator . IsNotNull ) )
{
return ;
}
var property = filter . Property . Replace ( '.' , '/' ) ;
2023-04-11 00:50:19 -05:00
2025-05-20 11:46:24 +03:00
var parameter = Expression . Parameter ( typeof ( T ) , "x" ) ;
var propertyExpression = GetNestedPropertyExpression ( parameter , filter . Property ) ;
if ( propertyExpression = = null ) return ;
var filterPropertyType = filter . Type ? ? propertyExpression . Type ;
2023-02-01 14:23:06 +02:00
2025-05-20 11:46:24 +03:00
if ( filterCaseSensitivity = = FilterCaseSensitivity . CaseInsensitive & & filterPropertyType = = typeof ( string ) )
2023-02-01 14:23:06 +02:00
{
property = $"tolower({property})" ;
}
if ( filter . FilterOperator = = FilterOperator . StartsWith | | filter . FilterOperator = = FilterOperator . EndsWith
| | filter . FilterOperator = = FilterOperator . Contains | | filter . FilterOperator = = FilterOperator . DoesNotContain )
{
2025-05-20 11:46:24 +03:00
if ( IsEnumerable ( filterPropertyType ) & & filterPropertyType ! = typeof ( string ) & &
2023-02-01 14:23:06 +02:00
( filter . FilterOperator = = FilterOperator . Contains | | filter . FilterOperator = = FilterOperator . DoesNotContain ) )
{
var enumerableValue = ( ( IEnumerable ) ( filter . FilterValue ! = null ? filter . FilterValue : Enumerable . Empty < object > ( ) ) ) . AsQueryable ( ) ;
2025-02-13 13:35:41 +02:00
var firstItemType = enumerableValue . Cast < object > ( ) . Any ( ) ? enumerableValue . FirstOrDefault ( ) . GetType ( ) : typeof ( object ) ;
2023-02-01 14:23:06 +02:00
var enumerableValueAsString = "(" + String . Join ( "," ,
2023-09-18 09:11:46 +03:00
( enumerableValue . ElementType = = typeof ( string ) | | firstItemType = = typeof ( string ) ? enumerableValue . Cast < string > ( ) . Select ( i = > $@"'{i}'" ) . Cast < object > ( ) : enumerableValue . Cast < object > ( ) ) ) + ")" ;
2023-02-01 14:23:06 +02:00
2025-02-13 13:35:41 +02:00
if ( enumerableValue . Cast < object > ( ) . Any ( ) & & filter . FilterOperator = = FilterOperator . Contains )
2023-02-01 14:23:06 +02:00
{
filterExpressions . Add ( $"{property} in {enumerableValueAsString}" ) ;
}
2025-02-13 13:35:41 +02:00
else if ( enumerableValue . Cast < object > ( ) . Any ( ) & & filter . FilterOperator = = FilterOperator . DoesNotContain )
2023-02-01 14:23:06 +02:00
{
filterExpressions . Add ( $"not({property} in {enumerableValueAsString})" ) ;
}
}
else
{
2025-05-20 11:46:24 +03:00
var expression = filterCaseSensitivity = = FilterCaseSensitivity . CaseInsensitive ?
2024-02-07 02:32:04 -05:00
$"{ODataFilterOperators[filter.FilterOperator.Value]}({property}, tolower('{filter.FilterValue}'))" :
$"{ODataFilterOperators[filter.FilterOperator.Value]}({property}, '{filter.FilterValue}')" ;
2023-02-01 14:23:06 +02:00
if ( filter . FilterOperator = = FilterOperator . DoesNotContain )
{
expression = $"not({expression})" ;
}
filterExpressions . Add ( expression ) ;
}
}
2023-04-11 00:50:19 -05:00
else
2023-02-01 14:23:06 +02:00
{
2025-05-20 11:46:24 +03:00
if ( IsEnumerable ( filterPropertyType ) & & filterPropertyType ! = typeof ( string ) )
2023-02-01 14:23:06 +02:00
return ;
var value = $"{filter.FilterValue}" ;
if ( filter . FilterOperator = = FilterOperator . IsNull | | filter . FilterOperator = = FilterOperator . IsNotNull )
{
value = $"null" ;
}
else if ( filter . FilterOperator = = FilterOperator . IsEmpty | | filter . FilterOperator = = FilterOperator . IsNotEmpty )
{
value = $"''" ;
}
2025-05-20 11:46:24 +03:00
else if ( filterPropertyType = = typeof ( string ) | | PropertyAccess . IsEnum ( filterPropertyType ) | | PropertyAccess . IsNullableEnum ( filterPropertyType ) )
2023-02-01 14:23:06 +02:00
{
value = $"'{value}'" ;
}
2025-05-20 11:46:24 +03:00
else if ( filterPropertyType = = typeof ( DateTime ) | | filterPropertyType = = typeof ( DateTime ? ) )
2023-02-01 14:23:06 +02:00
{
2023-07-25 08:49:42 +03:00
try
{
value = Convert . ToDateTime ( filter . FilterValue ) . ToString ( CultureInfo . InvariantCulture ) ;
}
catch
{
//
}
2023-09-12 08:42:47 +03:00
value = $"{DateTime.Parse(value, CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.RoundtripKind).ToString(" yyyy - MM - ddTHH : mm : ss . fffZ ", CultureInfo.InvariantCulture)}" ;
2023-02-01 14:23:06 +02:00
}
2025-05-20 11:46:24 +03:00
else if ( filterPropertyType = = typeof ( bool ) | | filterPropertyType = = typeof ( bool? ) )
2023-02-01 14:23:06 +02:00
{
value = $"{value?.ToLower()}" ;
}
2024-02-07 02:32:04 -05:00
filterExpressions . Add ( $@"{property} {ODataFilterOperators[filter.FilterOperator.Value]} {value}" ) ;
2023-02-01 14:23:06 +02:00
}
}
}
2021-10-07 11:35:29 +03:00
/// <summary>
/// Ases the o data enumerable.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source">The source.</param>
/// <returns>ODataEnumerable<T>.</returns>
2021-01-19 11:02:49 +02:00
public static ODataEnumerable < T > AsODataEnumerable < T > ( this IEnumerable < T > source )
{
return new ODataEnumerable < T > ( source ) ;
}
2021-06-21 15:49:08 +03:00
2021-10-07 11:35:29 +03:00
/// <summary>
/// Selects the many recursive.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source">The source.</param>
/// <param name="selector">The selector.</param>
/// <returns>IEnumerable<T>.</returns>
2021-06-21 15:49:08 +03:00
public static IEnumerable < T > SelectManyRecursive < T > ( this IEnumerable < T > source , Func < T , IEnumerable < T > > selector )
{
var result = source . SelectMany ( selector ) ;
if ( ! result . Any ( ) )
{
return result ;
}
return result . Concat ( result . SelectManyRecursive ( selector ) ) ;
}
2024-02-29 09:53:57 +01:00
private static List < RadzenDataGridColumn < T > > GetFilterableColumns < T > ( IEnumerable < RadzenDataGridColumn < T > > columns )
{
return columns
. Where ( c = > c . Filterable
& & c . FilterPropertyType ! = null
& & ( ! ( c . GetFilterValue ( ) = = null | | c . GetFilterValue ( ) as string = = string . Empty )
2024-04-08 10:08:49 +03:00
| | ! c . CanSetFilterValue ( )
2024-02-29 09:53:57 +01:00
| | c . HasCustomFilter ( ) )
& & c . GetFilterProperty ( ) ! = null )
. ToList ( ) ;
}
2021-01-19 11:02:49 +02:00
}
2025-05-07 13:11:30 +03:00
}