Skip to content

Commit 2c58c20

Browse files
DynamicExpressionParser - Handle indexed properties with any number of indices in expression (#950)
* DynamicExpressionParser - Handle indexed properties with any number of indices in expression * Adjust exception message when misusing indexer (e.g : incorrect number of parameters)
1 parent 1cc4ea0 commit 2c58c20

File tree

3 files changed

+95
-8
lines changed

3 files changed

+95
-8
lines changed

src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2353,20 +2353,22 @@ private Expression ParseElementAccess(Expression expr)
23532353
switch (_methodFinder.FindIndexer(expr.Type, args, out var mb))
23542354
{
23552355
case 0:
2356-
throw ParseError(errorPos, Res.NoApplicableIndexer,
2357-
TypeHelper.GetTypeName(expr.Type));
2356+
throw ParseError(errorPos, Res.NoApplicableIndexer, TypeHelper.GetTypeName(expr.Type), args.Length);
23582357

23592358
case 1:
23602359
var indexMethod = (MethodInfo)mb!;
2361-
var indexParameterType = indexMethod.GetParameters().First().ParameterType;
2360+
var indexMethodArguments = indexMethod.GetParameters();
23622361

2363-
var indexArgumentExpression = args[0]; // Indexer only has 1 parameter, so we can use args[0] here
2364-
if (indexParameterType != indexArgumentExpression.Type)
2362+
var indexArgumentExpressions = new Expression[args.Length];
2363+
for (var i = 0; i < indexMethodArguments.Length; ++i)
23652364
{
2366-
indexArgumentExpression = Expression.Convert(indexArgumentExpression, indexParameterType);
2365+
var indexParameterType = indexMethodArguments[i].ParameterType;
2366+
indexArgumentExpressions[i] = indexParameterType != args[i].Type
2367+
? Expression.Convert(args[i], indexParameterType)
2368+
: args[i];
23672369
}
23682370

2369-
return Expression.Call(expr, indexMethod, indexArgumentExpression);
2371+
return Expression.Call(expr, indexMethod, indexArgumentExpressions);
23702372

23712373
default:
23722374
throw ParseError(errorPos, Res.AmbiguousIndexerInvocation, TypeHelper.GetTypeName(expr.Type));

src/System.Linq.Dynamic.Core/Res.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ internal static class Res
5555
public const string MissingAsClause = "Expression is missing an 'as' clause";
5656
public const string NeitherTypeConvertsToOther = "Neither of the types '{0}' and '{1}' converts to the other";
5757
public const string NewOperatorIsNotAllowed = "Using the new operator is not allowed via the ParsingConfig.";
58-
public const string NoApplicableIndexer = "No applicable indexer exists in type '{0}'";
58+
public const string NoApplicableIndexer = "No applicable indexer exists in type '{0}' with {1} parameters";
5959
public const string NoApplicableMethod = "No applicable method '{0}' exists in type '{1}'";
6060
public const string NoItInScope = "No 'it' is in scope";
6161
public const string NoMatchingConstructor = "No matching constructor in type '{0}'";

test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,18 @@ public override HashSet<Type> GetCustomTypes()
7070
}
7171
}
7272

73+
private class ClassWithIndexers
74+
{
75+
public int this[int i1]
76+
{
77+
get => i1 + 1;
78+
}
79+
public string this[int i1, string i2]
80+
{
81+
get => i1 + "-" + i2;
82+
}
83+
}
84+
7385
private class ComplexParseLambda1Result
7486
{
7587
public int? Age;
@@ -660,6 +672,79 @@ public void DynamicExpressionParser_ParseLambda_Issue58()
660672
Check.That(result).Equals(42);
661673
}
662674

675+
[Fact]
676+
public void DynamicExpressionParser_ParseLambda_Indexer1D()
677+
{
678+
// Arrange
679+
var customTypeProvider = new Mock<IDynamicLinqCustomTypeProvider>();
680+
customTypeProvider.Setup(c => c.GetCustomTypes()).Returns([typeof(ClassWithIndexers)]);
681+
var config = new ParsingConfig
682+
{
683+
CustomTypeProvider = customTypeProvider.Object
684+
};
685+
var expressionParams = new[]
686+
{
687+
Expression.Parameter(typeof(ClassWithIndexers), "myObj")
688+
};
689+
690+
var myClassInstance = new ClassWithIndexers();
691+
var invokersMerge = new List<object> { myClassInstance };
692+
693+
// Act
694+
var expression = DynamicExpressionParser.ParseLambda(config, false, expressionParams, null, "myObj[3]");
695+
var del = expression.Compile();
696+
var result = del.DynamicInvoke(invokersMerge.ToArray());
697+
698+
// Assert
699+
Check.That(result).Equals(4);
700+
}
701+
702+
[Fact]
703+
public void DynamicExpressionParser_ParseLambda_Indexer2D()
704+
{
705+
// Arrange
706+
var customTypeProvider = new Mock<IDynamicLinqCustomTypeProvider>();
707+
customTypeProvider.Setup(c => c.GetCustomTypes()).Returns([typeof(ClassWithIndexers)]);
708+
var config = new ParsingConfig
709+
{
710+
CustomTypeProvider = customTypeProvider.Object
711+
};
712+
var expressionParams = new[]
713+
{
714+
Expression.Parameter(typeof(ClassWithIndexers), "myObj")
715+
};
716+
717+
var myClassInstance = new ClassWithIndexers();
718+
var invokersMerge = new List<object> { myClassInstance };
719+
720+
// Act
721+
var expression = DynamicExpressionParser.ParseLambda(config, false, expressionParams, null, "myObj[3,\"1\"]");
722+
var del = expression.Compile();
723+
var result = del.DynamicInvoke(invokersMerge.ToArray());
724+
725+
// Assert
726+
Check.That(result).Equals("3-1");
727+
}
728+
729+
[Fact]
730+
public void DynamicExpressionParser_ParseLambda_IndexerParameterMismatch()
731+
{
732+
// Arrange
733+
var customTypeProvider = new Mock<IDynamicLinqCustomTypeProvider>();
734+
customTypeProvider.Setup(c => c.GetCustomTypes()).Returns([typeof(ClassWithIndexers)]);
735+
var config = new ParsingConfig
736+
{
737+
CustomTypeProvider = customTypeProvider.Object
738+
};
739+
var expressionParams = new[]
740+
{
741+
Expression.Parameter(typeof(ClassWithIndexers), "myObj")
742+
};
743+
744+
Assert.Throws<ParseException>(() =>
745+
DynamicExpressionParser.ParseLambda(config, false, expressionParams, null, "myObj[3,\"1\",1]"));
746+
}
747+
663748
[Fact]
664749
public void DynamicExpressionParser_ParseLambda_DuplicateParameterNames_ThrowsException()
665750
{

0 commit comments

Comments
 (0)