33using System . Linq ;
44using System . Linq . Expressions ;
55using System . Reflection ;
6+ using System . Text . Json . Serialization ;
67using FluentResults ;
78
89public static class FilterEvaluator
@@ -127,11 +128,6 @@ private static Result<MemberExpression> ResolvePropertyPathForCollection(
127128 return ( MemberExpression ) current ;
128129 }
129130
130- private static bool IsNullableReferenceType ( Type type )
131- {
132- return ! type . IsValueType || Nullable . GetUnderlyingType ( type ) != null ;
133- }
134-
135131 private static bool IsPrimitiveType ( Type type )
136132 {
137133 return type . IsPrimitive || type == typeof ( string ) || type == typeof ( decimal ) ||
@@ -226,13 +222,13 @@ private static Result<ConstantExpression> CreateConstantExpression(QueryExpressi
226222 {
227223 return literal switch
228224 {
229- IntegerLiteral intLit => CreateIntegerConstant ( intLit . Value , expression ) ,
225+ IntegerLiteral intLit => CreateIntegerOrEnumConstant ( intLit . Value , expression . Type ) ,
230226 DateLiteral dateLit => Result . Ok ( CreateDateConstant ( dateLit , expression ) ) ,
231227 GuidLiteral guidLit => Result . Ok ( Expression . Constant ( guidLit . Value , expression . Type ) ) ,
232228 DecimalLiteral decLit => Result . Ok ( Expression . Constant ( decLit . Value , expression . Type ) ) ,
233229 FloatLiteral floatLit => Result . Ok ( Expression . Constant ( floatLit . Value , expression . Type ) ) ,
234230 DoubleLiteral dblLit => Result . Ok ( Expression . Constant ( dblLit . Value , expression . Type ) ) ,
235- StringLiteral strLit => Result . Ok ( Expression . Constant ( strLit . Value , expression . Type ) ) ,
231+ StringLiteral strLit => CreateStringOrEnumConstant ( strLit . Value , expression . Type ) ,
236232 DateTimeLiteral dtLit => Result . Ok ( Expression . Constant ( dtLit . Value , expression . Type ) ) ,
237233 BooleanLiteral boolLit => Result . Ok ( Expression . Constant ( boolLit . Value , expression . Type ) ) ,
238234 NullLiteral _ => Result . Ok ( Expression . Constant ( null , expression . Type ) ) ,
@@ -248,11 +244,6 @@ private static Result<ConstantExpression> CreateConstantExpression(QueryExpressi
248244 return Result . Ok ( ( constantResult . Value , property ) ) ;
249245 }
250246
251- private static Result < ConstantExpression > CreateIntegerConstant ( int value , Expression expression )
252- {
253- return GetIntegerExpressionConstant ( value , expression . Type ) ;
254- }
255-
256247 private static ConstantExpression CreateDateConstant ( DateLiteral dateLiteral , Expression expression )
257248 {
258249 if ( expression . Type == typeof ( DateTime ? ) )
@@ -563,9 +554,7 @@ private static Result<ConstantExpression> GetIntegerExpressionConstant(int value
563554 {
564555 try
565556 {
566- // Fetch the underlying type if it's nullable.
567- var underlyingType = Nullable . GetUnderlyingType ( targetType ) ;
568- var type = underlyingType ?? targetType ;
557+ var type = GetNonNullableType ( targetType ) ;
569558
570559 object convertedValue = type switch
571560 {
@@ -586,9 +575,76 @@ private static Result<ConstantExpression> GetIntegerExpressionConstant(int value
586575 {
587576 return Result . Fail ( $ "Value { value } is too large for type { targetType . Name } ") ;
588577 }
589- catch ( Exception ex )
578+ catch ( Exception )
579+ {
580+ return Result . Fail ( $ "Error converting { value } to { targetType . Name } ") ;
581+ }
582+ }
583+
584+ private static Result < ConstantExpression > CreateIntegerOrEnumConstant ( int value , Type targetType )
585+ {
586+ var actualType = GetNonNullableType ( targetType ) ;
587+
588+ if ( actualType . IsEnum )
589+ {
590+ return ConvertIntegerToEnum ( value , actualType , targetType ) ;
591+ }
592+
593+ return GetIntegerExpressionConstant ( value , targetType ) ;
594+ }
595+
596+ private static Result < ConstantExpression > ConvertIntegerToEnum ( int value , Type actualType , Type targetType )
597+ {
598+ try
599+ {
600+ var enumValue = Enum . ToObject ( actualType , value ) ;
601+
602+ return Result . Ok ( Expression . Constant ( enumValue , targetType ) ) ;
603+ }
604+ catch ( Exception )
590605 {
591- return Result . Fail ( $ "Error converting { value } to { targetType . Name } : { ex . Message } ") ;
606+ return Result . Fail ( $ "Error converting { value } to enum type { targetType . Name } ") ;
592607 }
593608 }
594- }
609+
610+ private static Result < ConstantExpression > CreateStringOrEnumConstant ( string value , Type targetType )
611+ {
612+ var actualType = GetNonNullableType ( targetType ) ;
613+
614+ if ( actualType . IsEnum )
615+ {
616+ return ConvertStringToEnum ( value , actualType , targetType ) ;
617+ }
618+
619+ return Result . Ok ( Expression . Constant ( value , targetType ) ) ;
620+ }
621+
622+ private static Result < ConstantExpression > ConvertStringToEnum ( string value , Type actualType , Type targetType )
623+ {
624+ try
625+ {
626+ var enumValue = Enum . Parse ( actualType , value , true ) ;
627+
628+ return Result . Ok ( Expression . Constant ( enumValue , targetType ) ) ;
629+ }
630+ catch ( Exception )
631+ {
632+ foreach ( var field in actualType . GetFields ( BindingFlags . Public | BindingFlags . Static ) )
633+ {
634+ var memberNameAttribute = field . GetCustomAttribute < JsonStringEnumMemberNameAttribute > ( ) ;
635+
636+ if ( memberNameAttribute != null && memberNameAttribute . Name . Equals ( value , StringComparison . Ordinal ) )
637+ {
638+ return Result . Ok ( Expression . Constant ( field . GetValue ( null ) , targetType ) ) ;
639+ }
640+ }
641+
642+ return Result . Fail ( $ "Value '{ value } ' is not a valid member of enum { actualType . Name } ") ;
643+ }
644+ }
645+
646+ private static Type GetNonNullableType ( Type type )
647+ {
648+ return Nullable . GetUnderlyingType ( type ) ?? type ;
649+ }
650+ }
0 commit comments