Skip to content

Commit 13ecca6

Browse files
authored
Add support for SequenceEqual (#860)
* Add support for SequenceEqual * sc * .
1 parent 3ea784d commit 13ecca6

File tree

2 files changed

+54
-23
lines changed

2 files changed

+54
-23
lines changed

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

+23-23
Original file line numberDiff line numberDiff line change
@@ -1820,13 +1820,11 @@ private Expression ParseMemberAccess(Type? type, Expression? expression, string?
18201820
var isStringWithStringMethod = type == typeof(string) && _methodFinder.ContainsMethod(type, id, isStaticAccess);
18211821
var isApplicableForEnumerable = !isStaticAccess && !isConstantString && !isStringWithStringMethod;
18221822

1823-
if (isApplicableForEnumerable && TypeHelper.TryFindGenericType(typeof(IEnumerable<>), type, out var enumerableType))
1823+
if (isApplicableForEnumerable &&
1824+
TypeHelper.TryFindGenericType(typeof(IEnumerable<>), type, out var enumerableType) &&
1825+
TryParseEnumerable(expression!, enumerableType, id, type, out args, out var enumerableExpression))
18241826
{
1825-
var elementType = enumerableType.GetTypeInfo().GetGenericTypeArguments()[0];
1826-
if (TryParseEnumerable(expression!, elementType, id, errorPos, type, out args, out var enumerableExpression))
1827-
{
1828-
return enumerableExpression;
1829-
}
1827+
return enumerableExpression;
18301828
}
18311829

18321830
// If args is not set by TryParseEnumerable (in case when the expression is not an Enumerable), do parse the argument list here.
@@ -2061,8 +2059,10 @@ private Expression ParseAsEnumOrNestedClass(string id)
20612059
return ParseMemberAccess(type, null, identifier);
20622060
}
20632061

2064-
private bool TryParseEnumerable(Expression instance, Type elementType, string methodName, int errorPos, Type? type, out Expression[]? args, [NotNullWhen(true)] out Expression? expression)
2062+
private bool TryParseEnumerable(Expression instance, Type enumerableType, string methodName, Type? type, out Expression[]? args, [NotNullWhen(true)] out Expression? expression)
20652063
{
2064+
var elementType = enumerableType.GetTypeInfo().GetGenericTypeArguments()[0];
2065+
20662066
// Keep the current _parent.
20672067
var oldParent = _parent;
20682068

@@ -2124,7 +2124,7 @@ private bool TryParseEnumerable(Expression instance, Type elementType, string me
21242124
// #633 - For Average without any arguments, try to find the non-generic Average method on the callType for the supplied parameter type.
21252125
if (methodName == nameof(Enumerable.Average) && args.Length == 0 && _methodFinder.TryFindAverageMethod(callType, theType, out var averageMethod))
21262126
{
2127-
expression = Expression.Call(null, averageMethod, new[] { instance });
2127+
expression = Expression.Call(null, averageMethod, instance);
21282128
return true;
21292129
}
21302130

@@ -2136,56 +2136,56 @@ private bool TryParseEnumerable(Expression instance, Type elementType, string me
21362136
throw ParseError(_textParser.CurrentToken.Pos, Res.FunctionRequiresOneArg, methodName);
21372137
}
21382138

2139-
typeArgs = new[] { ResolveTypeFromArgumentExpression(methodName, args[0]) };
2140-
args = new Expression[0];
2139+
typeArgs = [ResolveTypeFromArgumentExpression(methodName, args[0])];
2140+
args = [];
21412141
}
21422142
else if (new[] { "Max", "Min", "Select", "OrderBy", "OrderByDescending", "ThenBy", "ThenByDescending", "GroupBy" }.Contains(methodName))
21432143
{
21442144
if (args.Length == 2)
21452145
{
2146-
typeArgs = new[] { elementType, args[0].Type, args[1].Type };
2146+
typeArgs = [elementType, args[0].Type, args[1].Type];
21472147
}
21482148
else if (args.Length == 1)
21492149
{
2150-
typeArgs = new[] { elementType, args[0].Type };
2150+
typeArgs = [elementType, args[0].Type];
21512151
}
21522152
else
21532153
{
2154-
typeArgs = new[] { elementType };
2154+
typeArgs = [elementType];
21552155
}
21562156
}
21572157
else if (methodName == "SelectMany")
21582158
{
21592159
var bodyType = Expression.Lambda(args[0], innerIt).Body.Type;
2160-
var interfaces = bodyType.GetInterfaces().Union(new[] { bodyType });
2161-
Type interfaceType = interfaces.Single(i => i.Name == typeof(IEnumerable<>).Name);
2162-
Type resultType = interfaceType.GetTypeInfo().GetGenericTypeArguments()[0];
2163-
typeArgs = new[] { elementType, resultType };
2160+
var interfaces = bodyType.GetInterfaces().Union([bodyType]);
2161+
var interfaceType = interfaces.Single(i => i.Name == typeof(IEnumerable<>).Name);
2162+
var resultType = interfaceType.GetTypeInfo().GetGenericTypeArguments()[0];
2163+
typeArgs = [elementType, resultType];
21642164
}
21652165
else
21662166
{
2167-
typeArgs = new[] { elementType };
2167+
typeArgs = [elementType];
21682168
}
21692169

21702170
if (args.Length == 0)
21712171
{
2172-
args = new[] { instance };
2172+
args = [instance];
21732173
}
21742174
else
21752175
{
2176-
if (new[] { "Concat", "Contains", "ContainsKey", "DefaultIfEmpty", "Except", "Intersect", "Skip", "Take", "Union" }.Contains(methodName))
2176+
if (new[] { "Concat", "Contains", "ContainsKey", "DefaultIfEmpty", "Except", "Intersect", "Skip", "Take", "Union", "SequenceEqual" }.Contains(methodName))
21772177
{
2178-
args = new[] { instance, args[0] };
2178+
args = [instance, args[0]];
21792179
}
21802180
else
21812181
{
21822182
if (args.Length == 2)
21832183
{
2184-
args = new[] { instance, Expression.Lambda(args[0], innerIt), Expression.Lambda(args[1], innerIt) };
2184+
args = [instance, Expression.Lambda(args[0], innerIt), Expression.Lambda(args[1], innerIt)];
21852185
}
21862186
else
21872187
{
2188-
args = new[] { instance, Expression.Lambda(args[0], innerIt) };
2188+
args = [instance, Expression.Lambda(args[0], innerIt)];
21892189
}
21902190
}
21912191
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System.Linq.Dynamic.Core.Parser;
2+
using System.Linq.Expressions;
3+
using Xunit;
4+
5+
namespace System.Linq.Dynamic.Core.Tests.Parser;
6+
7+
partial class ExpressionParserTests
8+
{
9+
[Fact]
10+
public void Parse_SequenceEqual()
11+
{
12+
// Arrange
13+
var parameter = Expression.Parameter(typeof(Entity), "Entity");
14+
15+
var parser = new ExpressionParser(
16+
[parameter],
17+
"Entity.ArrayA.SequenceEqual(Entity.ArrayB)",
18+
null,
19+
null);
20+
21+
// Act
22+
parser.Parse(typeof(bool));
23+
}
24+
25+
public class Entity
26+
{
27+
public string[] ArrayA { get; set; } = [];
28+
29+
public string[] ArrayB { get; set; } = [];
30+
}
31+
}

0 commit comments

Comments
 (0)