Skip to content

Commit 5351719

Browse files
authored
Support 'System.Type' in As, Is, Cast and OfType (#467)
1 parent 50467c4 commit 5351719

File tree

5 files changed

+126
-4
lines changed

5 files changed

+126
-4
lines changed

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

+24-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using JetBrains.Annotations;
2-
using System.Collections;
1+
using System.Collections;
32
using System.Collections.Generic;
43
using System.ComponentModel;
54
using System.Globalization;
@@ -11,6 +10,7 @@
1110
using System.Linq.Dynamic.Core.Validation;
1211
using System.Linq.Expressions;
1312
using System.Reflection;
13+
using JetBrains.Annotations;
1414

1515
namespace System.Linq.Dynamic.Core.Parser
1616
{
@@ -1861,7 +1861,28 @@ Expression ParseEnumerable(Expression instance, Type elementType, string methodN
18611861

18621862
private Type ResolveTypeFromArgumentExpression(string functionName, Expression argumentExpression)
18631863
{
1864-
string typeName = (argumentExpression as ConstantExpression)?.Value as string;
1864+
switch (argumentExpression)
1865+
{
1866+
case ConstantExpression constantExpression:
1867+
switch (constantExpression.Value)
1868+
{
1869+
case string typeName:
1870+
return ResolveTypeStringFromArgument(functionName, typeName);
1871+
1872+
case Type type:
1873+
return type;
1874+
1875+
default:
1876+
throw ParseError(_textParser.CurrentToken.Pos, Res.FunctionRequiresOneNotNullArgOfType, functionName, "string or System.Type");
1877+
}
1878+
1879+
default:
1880+
throw ParseError(_textParser.CurrentToken.Pos, Res.FunctionRequiresOneNotNullArgOfType, functionName, "ConstantExpression");
1881+
}
1882+
}
1883+
1884+
private Type ResolveTypeStringFromArgument(string functionName, string typeName)
1885+
{
18651886
if (string.IsNullOrEmpty(typeName))
18661887
{
18671888
throw ParseError(_textParser.CurrentToken.Pos, Res.FunctionRequiresOneNotNullArg, functionName, typeName);

src/System.Linq.Dynamic.Core/Parser/SupportedMethods/IEnumerableSignatures.cs

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ internal interface IEnumerableSignatures
1616
void Average(long? selector);
1717
void Average(long selector);
1818
void Cast(string type);
19+
void Cast(Type type);
1920
void Contains(object selector);
2021
void Count();
2122
void Count(bool predicate);
@@ -37,6 +38,7 @@ internal interface IEnumerableSignatures
3738
void Max(object selector);
3839
void Min(object selector);
3940
void OfType(string type);
41+
void OfType(Type type);
4042
void OrderBy(object selector);
4143
void OrderByDescending(object selector);
4244
void Select(object selector);

src/System.Linq.Dynamic.Core/Parser/SupportedMethods/IQueryableSignatures.cs

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ internal interface IQueryableSignatures
1616
void Average(long? selector);
1717
void Average(long selector);
1818
void Cast(string type);
19+
void Cast(Type type);
1920
void Count();
2021
void Count(bool predicate);
2122
void DefaultIfEmpty();
@@ -36,6 +37,7 @@ internal interface IQueryableSignatures
3637
void Max(object selector);
3738
void Min(object selector);
3839
void OfType(string type);
40+
void OfType(Type type);
3941
void OrderBy(object selector);
4042
void OrderByDescending(object selector);
4143
void Select(object selector);

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

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ internal static class Res
2424
public const string FirstExprMustBeBool = "The first expression must be of type 'Boolean'";
2525
public const string FunctionRequiresOneArg = "The '{0}' function requires one argument";
2626
public const string FunctionRequiresOneNotNullArg = "The '{0}' function requires one argument which is not null.";
27+
public const string FunctionRequiresOneNotNullArgOfType = "The '{0}' function requires one argument of type {1} which is not null.";
2728
public const string HexCharExpected = "Hexadecimal character expected";
2829
public const string IQueryableProviderNotAsync = "The provider for the source IQueryable doesn't implement IAsyncQueryProvider/IDbAsyncQueryProvider. Only providers that implement IAsyncQueryProvider/IDbAsyncQueryProvider can be used for Entity Framework asynchronous operations.";
2930
public const string IdentifierExpected = "Identifier expected";

test/System.Linq.Dynamic.Core.Tests/QueryableTests.Is,OfType,As,Cast.cs

+97-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public void OfType_WithString()
4242
}
4343

4444
[Fact]
45-
public void OfType_Dynamic()
45+
public void OfType_Dynamic_WithFullName()
4646
{
4747
// Assign
4848
var qry = new[]
@@ -64,6 +64,29 @@ public void OfType_Dynamic()
6464
Check.That(oftypeDynamic.Length).Equals(oftype.Length);
6565
}
6666

67+
[Fact]
68+
public void OfType_Dynamic_WithType()
69+
{
70+
// Assign
71+
var qry = new[]
72+
{
73+
new CompanyWithBaseEmployees
74+
{
75+
Employees = new BaseEmployee[]
76+
{
77+
new Worker { Name = "e" }, new Boss { Name = "e" }
78+
}
79+
}
80+
}.AsQueryable();
81+
82+
// Act
83+
var oftype = qry.Select(c => c.Employees.OfType<Worker>().Where(e => e.Name == "e")).ToArray();
84+
var oftypeDynamic = qry.Select("Employees.OfType(@0).Where(Name == \"e\")", typeof(Worker)).ToDynamicArray();
85+
86+
// Assert
87+
Check.That(oftypeDynamic.Length).Equals(oftype.Length);
88+
}
89+
6790
[Fact]
6891
public void Is_Dynamic_ActingOnIt()
6992
{
@@ -103,6 +126,23 @@ public void Is_Dynamic_ActingOnIt_WithSimpleName()
103126
Check.That(countOfTypeDynamic).Equals(countOfType);
104127
}
105128

129+
[Fact]
130+
public void Is_Dynamic_ActingOnIt_WithType()
131+
{
132+
// Assign
133+
var qry = new BaseEmployee[]
134+
{
135+
new Worker { Name = "1" }, new Boss { Name = "b" }
136+
}.AsQueryable();
137+
138+
// Act
139+
int countOfType = qry.Count(c => c is Worker);
140+
int countOfTypeDynamic = qry.Count("is(@0)", typeof(Worker));
141+
142+
// Assert
143+
Check.That(countOfTypeDynamic).Equals(countOfType);
144+
}
145+
106146
[Fact]
107147
public void As_Dynamic_ActingOnIt()
108148
{
@@ -119,6 +159,22 @@ public void As_Dynamic_ActingOnIt()
119159
Check.That(countAsDynamic).Equals(1);
120160
}
121161

162+
[Fact]
163+
public void As_Dynamic_ActingOnIt_WithType()
164+
{
165+
// Assign
166+
var qry = new BaseEmployee[]
167+
{
168+
new Worker { Name = "1" }, new Boss { Name = "b" }
169+
}.AsQueryable();
170+
171+
// Act
172+
int countAsDynamic = qry.Count("As(@0) != null", typeof(Worker));
173+
174+
// Assert
175+
Check.That(countAsDynamic).Equals(1);
176+
}
177+
122178
[Fact]
123179
public void CastToType_WithType()
124180
{
@@ -176,6 +232,29 @@ public void CastToType_Dynamic()
176232
Check.That(cast.Length).Equals(castDynamic.Length);
177233
}
178234

235+
[Fact]
236+
public void CastToType_Dynamic_WithType()
237+
{
238+
// Assign
239+
var qry = new[]
240+
{
241+
new CompanyWithBaseEmployees
242+
{
243+
Employees = new BaseEmployee[]
244+
{
245+
new Worker { Name = "e" }
246+
}
247+
}
248+
}.AsQueryable();
249+
250+
// Act
251+
var cast = qry.Select(c => c.Employees.Cast<Worker>().Where(e => e.Name == "e")).ToArray();
252+
var castDynamic = qry.Select("Employees.Cast(@0).Where(Name == \"e\")", typeof(Worker)).ToDynamicArray();
253+
254+
// Assert
255+
Check.That(cast.Length).Equals(castDynamic.Length);
256+
}
257+
179258
[Fact]
180259
public void CastToType_Dynamic_ActingOnIt()
181260
{
@@ -193,6 +272,23 @@ public void CastToType_Dynamic_ActingOnIt()
193272
Check.That(cast.Length).Equals(castDynamic.Length);
194273
}
195274

275+
[Fact]
276+
public void CastToType_Dynamic_ActingOnIt_WithType()
277+
{
278+
// Assign
279+
var qry = new BaseEmployee[]
280+
{
281+
new Worker { Name = "1" }, new Worker { Name = "2" }
282+
}.AsQueryable();
283+
284+
// Act
285+
var cast = qry.Select(c => (Worker)c).ToArray();
286+
var castDynamic = qry.Select("Cast(@0)", typeof(Worker)).ToDynamicArray();
287+
288+
// Assert
289+
Check.That(cast.Length).Equals(castDynamic.Length);
290+
}
291+
196292
[Fact]
197293
public void CastToType_Dynamic_ActingOnIt_Throws()
198294
{

0 commit comments

Comments
 (0)