1
1
using System . Collections ;
2
2
using System . Collections . Generic ;
3
3
using System . ComponentModel ;
4
+ using System . Diagnostics . CodeAnalysis ;
4
5
using System . Globalization ;
5
6
using System . Linq . Dynamic . Core . Exceptions ;
6
7
using System . Linq . Dynamic . Core . Parser . SupportedMethods ;
@@ -19,10 +20,10 @@ namespace System.Linq.Dynamic.Core.Parser;
19
20
/// </summary>
20
21
public class ExpressionParser
21
22
{
22
- private static readonly string methodOrderBy = nameof ( Queryable . OrderBy ) ;
23
- private static readonly string methodOrderByDescending = nameof ( Queryable . OrderByDescending ) ;
24
- private static readonly string methodThenBy = nameof ( Queryable . ThenBy ) ;
25
- private static readonly string methodThenByDescending = nameof ( Queryable . ThenByDescending ) ;
23
+ private const string MethodOrderBy = nameof ( Queryable . OrderBy ) ;
24
+ private const string MethodOrderByDescending = nameof ( Queryable . OrderByDescending ) ;
25
+ private const string MethodThenBy = nameof ( Queryable . ThenBy ) ;
26
+ private const string MethodThenByDescending = nameof ( Queryable . ThenByDescending ) ;
26
27
27
28
private readonly ParsingConfig _parsingConfig ;
28
29
private readonly MethodFinder _methodFinder ;
@@ -51,7 +52,7 @@ public class ExpressionParser
51
52
/// There was a problem when an expression contained multiple lambdas where
52
53
/// the ItName was not cleared and freed for the next lambda. This variable
53
54
/// stores the ItName of the last parsed lambda.
54
- /// Not used internally by ExpressionParser, but used to preserve compatiblity of parsingConfig.RenameParameterExpression
55
+ /// Not used internally by ExpressionParser, but used to preserve compatibility of parsingConfig.RenameParameterExpression
55
56
/// which was designed to only work with mono-lambda expressions.
56
57
/// </summary>
57
58
public string LastLambdaItName { get ; private set ; } = KeywordsHelper . KEYWORD_IT ;
@@ -93,7 +94,7 @@ private void ProcessParameters(ParameterExpression[] parameters)
93
94
{
94
95
foreach ( ParameterExpression pe in parameters . Where ( p => ! string . IsNullOrEmpty ( p . Name ) ) )
95
96
{
96
- AddSymbol ( pe . Name , pe ) ;
97
+ AddSymbol ( pe . Name ! , pe ) ;
97
98
}
98
99
99
100
// If there is only 1 ParameterExpression, do also allow access using 'it'
@@ -185,11 +186,11 @@ internal IList<DynamicOrdering> ParseOrdering(bool forceThenBy = false)
185
186
string methodName ;
186
187
if ( forceThenBy || orderings . Count > 0 )
187
188
{
188
- methodName = ascending ? methodThenBy : methodThenByDescending ;
189
+ methodName = ascending ? MethodThenBy : MethodThenByDescending ;
189
190
}
190
191
else
191
192
{
192
- methodName = ascending ? methodOrderBy : methodOrderByDescending ;
193
+ methodName = ascending ? MethodOrderBy : MethodOrderByDescending ;
193
194
}
194
195
195
196
orderings . Add ( new DynamicOrdering { Selector = expr , Ascending = ascending , MethodName = methodName } ) ;
@@ -404,11 +405,11 @@ private Expression ParseLogicalAndOrOperator()
404
405
switch ( op . Id )
405
406
{
406
407
case TokenId . Ampersand :
407
- if ( left . Type == typeof ( string ) && left . NodeType == ExpressionType . Constant && int . TryParse ( ( string ) ( ( ConstantExpression ) left ) . Value , out var parseValue ) && TypeHelper . IsNumericType ( right . Type ) )
408
+ if ( left . Type == typeof ( string ) && left . NodeType == ExpressionType . Constant && int . TryParse ( ( string ? ) ( ( ConstantExpression ) left ) . Value , out var parseValue ) && TypeHelper . IsNumericType ( right . Type ) )
408
409
{
409
410
left = Expression . Constant ( parseValue ) ;
410
411
}
411
- else if ( right . Type == typeof ( string ) && right . NodeType == ExpressionType . Constant && int . TryParse ( ( string ) ( ( ConstantExpression ) right ) . Value , out parseValue ) && TypeHelper . IsNumericType ( left . Type ) )
412
+ else if ( right . Type == typeof ( string ) && right . NodeType == ExpressionType . Constant && int . TryParse ( ( string ? ) ( ( ConstantExpression ) right ) . Value , out parseValue ) && TypeHelper . IsNumericType ( left . Type ) )
412
413
{
413
414
right = Expression . Constant ( parseValue ) ;
414
415
}
@@ -1172,7 +1173,14 @@ private Expression ParseFunctionCast()
1172
1173
it = args [ 0 ] ;
1173
1174
}
1174
1175
1175
- return Expression . ConvertChecked ( it , ResolveTypeFromArgumentExpression ( functionName , typeArgument , args . Length ) ) ;
1176
+ var destinationType = ResolveTypeFromArgumentExpression ( functionName , typeArgument , args . Length ) ;
1177
+
1178
+ if ( TryGenerateConversion ( it , destinationType , out var conversionExpression ) )
1179
+ {
1180
+ return conversionExpression ;
1181
+ }
1182
+
1183
+ return Expression . ConvertChecked ( it , destinationType ) ;
1176
1184
}
1177
1185
1178
1186
private Expression GenerateConditional ( Expression test , Expression expressionIfTrue , Expression expressionIfFalse , bool nullPropagating , int errorPos )
@@ -1360,9 +1368,9 @@ private Expression ParseNew()
1360
1368
&& methodCallExpression . Arguments . Count == 1
1361
1369
&& methodCallExpression . Arguments [ 0 ] is ConstantExpression methodCallExpressionArgument
1362
1370
&& methodCallExpressionArgument . Type == typeof ( string )
1363
- && properties . All ( x => x . Name != ( string ) methodCallExpressionArgument . Value ) )
1371
+ && properties . All ( x => x . Name != ( string ? ) methodCallExpressionArgument . Value ) )
1364
1372
{
1365
- propName = ( string ) methodCallExpressionArgument . Value ;
1373
+ propName = ( string ? ) methodCallExpressionArgument . Value ;
1366
1374
}
1367
1375
else
1368
1376
{
@@ -1442,7 +1450,7 @@ private Expression CreateNewExpression(List<DynamicProperty> properties, List<Ex
1442
1450
// Create an expression tree that represents creating and initializing a one-dimensional array of type KeyValuePair<string, object>.
1443
1451
NewArrayExpression newArrayExpression = Expression . NewArrayInit ( typeof ( KeyValuePair < string , object > ) , arrayIndexParams ) ;
1444
1452
1445
- // Get the "public DynamicClass(KeyValuePair<string, object>[] propertylist )" constructor
1453
+ // Get the "public DynamicClass(KeyValuePair<string, object>[])" constructor
1446
1454
ConstructorInfo constructor = type . GetTypeInfo ( ) . DeclaredConstructors . First ( ) ;
1447
1455
1448
1456
return Expression . New ( constructor , newArrayExpression ) ;
@@ -1621,7 +1629,7 @@ private Expression ParseTypeAccess(Type type, bool getNext)
1621
1629
case 0 :
1622
1630
if ( args . Length == 1 && TryGenerateConversion ( args [ 0 ] , type , out generatedExpression ) )
1623
1631
{
1624
- return generatedExpression ! ;
1632
+ return generatedExpression ;
1625
1633
}
1626
1634
1627
1635
throw ParseError ( errorPos , Res . NoMatchingConstructor , TypeHelper . GetTypeName ( type ) ) ;
@@ -1640,56 +1648,61 @@ private Expression ParseTypeAccess(Type type, bool getNext)
1640
1648
return ParseMemberAccess ( type , null ) ;
1641
1649
}
1642
1650
1643
- private bool TryGenerateConversion ( Expression sourceExpression , Type type , out Expression ? expression )
1651
+ private bool TryGenerateConversion ( Expression sourceExpression , Type destinationType , [ NotNullWhen ( true ) ] out Expression ? expression )
1644
1652
{
1645
1653
Type exprType = sourceExpression . Type ;
1646
- if ( exprType == type )
1654
+ if ( exprType == destinationType )
1647
1655
{
1648
1656
expression = sourceExpression ;
1649
1657
return true ;
1650
1658
}
1651
1659
1652
- if ( exprType . GetTypeInfo ( ) . IsValueType && type . GetTypeInfo ( ) . IsValueType )
1660
+ if ( exprType . GetTypeInfo ( ) . IsValueType && destinationType . GetTypeInfo ( ) . IsValueType )
1653
1661
{
1654
- if ( ( TypeHelper . IsNullableType ( exprType ) || TypeHelper . IsNullableType ( type ) ) && TypeHelper . GetNonNullableType ( exprType ) == TypeHelper . GetNonNullableType ( type ) )
1662
+ if ( ( TypeHelper . IsNullableType ( exprType ) || TypeHelper . IsNullableType ( destinationType ) ) && TypeHelper . GetNonNullableType ( exprType ) == TypeHelper . GetNonNullableType ( destinationType ) )
1655
1663
{
1656
- expression = Expression . Convert ( sourceExpression , type ) ;
1664
+ expression = Expression . Convert ( sourceExpression , destinationType ) ;
1657
1665
return true ;
1658
1666
}
1659
1667
1660
- if ( ( TypeHelper . IsNumericType ( exprType ) || TypeHelper . IsEnumType ( exprType ) ) && TypeHelper . IsNumericType ( type ) || TypeHelper . IsEnumType ( type ) )
1668
+ if ( ( TypeHelper . IsNumericType ( exprType ) || TypeHelper . IsEnumType ( exprType ) ) && TypeHelper . IsNumericType ( destinationType ) || TypeHelper . IsEnumType ( destinationType ) )
1661
1669
{
1662
- expression = Expression . ConvertChecked ( sourceExpression , type ) ;
1670
+ expression = Expression . ConvertChecked ( sourceExpression , destinationType ) ;
1663
1671
return true ;
1664
1672
}
1665
1673
}
1666
1674
1667
- if ( exprType . IsAssignableFrom ( type ) || type . IsAssignableFrom ( exprType ) || exprType . GetTypeInfo ( ) . IsInterface || type . GetTypeInfo ( ) . IsInterface )
1675
+ if ( exprType . IsAssignableFrom ( destinationType ) || destinationType . IsAssignableFrom ( exprType ) || exprType . GetTypeInfo ( ) . IsInterface || destinationType . GetTypeInfo ( ) . IsInterface )
1668
1676
{
1669
- expression = Expression . Convert ( sourceExpression , type ) ;
1677
+ expression = Expression . Convert ( sourceExpression , destinationType ) ;
1670
1678
return true ;
1671
1679
}
1672
1680
1673
1681
// Try to Parse the string rather than just generate the convert statement
1674
- if ( sourceExpression . NodeType == ExpressionType . Constant && exprType == typeof ( string ) )
1682
+ if ( sourceExpression is ConstantExpression { Value : string constantStringValue } )
1675
1683
{
1676
- string text = ( string ) ( ( ConstantExpression ) sourceExpression ) . Value ;
1677
-
1678
- var typeConvertor = _typeConverterFactory . GetConverter ( type ) ;
1679
- // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
1680
- if ( typeConvertor != null && typeConvertor . CanConvertFrom ( typeof ( string ) ) )
1684
+ var typeConvertor = _typeConverterFactory . GetConverter ( destinationType ) ;
1685
+ if ( typeConvertor . CanConvertFrom ( typeof ( string ) ) )
1681
1686
{
1682
- var value = typeConvertor . ConvertFromInvariantString ( text ) ;
1683
- expression = Expression . Constant ( value , type ) ;
1687
+ var value = typeConvertor . ConvertFromInvariantString ( constantStringValue ) ;
1688
+ expression = Expression . Constant ( value , destinationType ) ;
1684
1689
return true ;
1685
1690
}
1686
1691
}
1687
1692
1688
1693
// Check if there are any explicit conversion operators on the source type which fit the requirement (cast to the return type).
1689
- bool explicitOperatorAvailable = exprType . GetTypeInfo ( ) . GetDeclaredMethods ( "op_Explicit" ) . Any ( m => m . ReturnType == type ) ;
1694
+ bool explicitOperatorAvailable = exprType . GetTypeInfo ( ) . GetDeclaredMethods ( "op_Explicit" ) . Any ( m => m . ReturnType == destinationType ) ;
1690
1695
if ( explicitOperatorAvailable )
1691
1696
{
1692
- expression = Expression . Convert ( sourceExpression , type ) ;
1697
+ expression = Expression . Convert ( sourceExpression , destinationType ) ;
1698
+ return true ;
1699
+ }
1700
+
1701
+ // Try to find a destinationType.Parse(...) method for the specific sourceExpression Type.
1702
+ var parseMethod = destinationType . GetMethod ( "Parse" , new Type [ ] { sourceExpression . Type } ) ;
1703
+ if ( parseMethod != null )
1704
+ {
1705
+ expression = Expression . Call ( parseMethod , sourceExpression ) ;
1693
1706
return true ;
1694
1707
}
1695
1708
@@ -1788,7 +1801,7 @@ private Expression ParseMemberAccess(Type? type, Expression? expression)
1788
1801
if ( type == typeof ( object ) )
1789
1802
{
1790
1803
// The member is a dynamic or ExpandoObject, so convert this
1791
- return _expressionHelper . ConvertToExpandoObjectAndCreateDynamicExpression ( expression , type , id ) ;
1804
+ return _expressionHelper . ConvertToExpandoObjectAndCreateDynamicExpression ( expression ! , type , id ) ;
1792
1805
}
1793
1806
#endif
1794
1807
// Parse as Lambda
@@ -1798,7 +1811,7 @@ private Expression ParseMemberAccess(Type? type, Expression? expression)
1798
1811
}
1799
1812
1800
1813
// This could be enum like "A.B.C.MyEnum.Value1" or "A.B.C+MyEnum.Value1"
1801
- if ( _textParser . CurrentToken . Id == TokenId . Dot || _textParser . CurrentToken . Id == TokenId . Plus )
1814
+ if ( _textParser . CurrentToken . Id is TokenId . Dot or TokenId . Plus )
1802
1815
{
1803
1816
return ParseAsEnum ( id ) ;
1804
1817
}
0 commit comments