Skip to content

Commit e7a3b06

Browse files
authored
Merge pull request #10 from PoweredSoft/feature/better-enumerable-support
v1.1.11
2 parents 7d9755e + a0c59a6 commit e7a3b06

6 files changed

Lines changed: 147 additions & 15 deletions

File tree

PoweredSoft.DynamicLinq.Test/HelpersTests.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,29 @@
77

88
namespace PoweredSoft.DynamicLinq.Test
99
{
10+
class Foo
11+
{
12+
13+
}
14+
15+
class ListOfFoo : List<Foo>
16+
{
17+
18+
}
19+
1020
[TestClass]
1121
public class HelpersTests
1222
{
1323

24+
[TestMethod]
25+
public void TestInheritanceOfListAsGenericEnumerableType()
26+
{
27+
var shouldBeTrue = QueryableHelpers.IsGenericEnumerable(typeof(ListOfFoo));
28+
Assert.IsTrue(shouldBeTrue);
29+
var type = QueryableHelpers.GetTypeOfEnumerable(typeof(ListOfFoo), true);
30+
Assert.IsTrue(type == typeof(Foo));
31+
}
32+
1433
[TestMethod]
1534
public void TestCreateFilterExpression()
1635
{

PoweredSoft.DynamicLinq.Test/SelectTests.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,69 @@ public class MockB
2323
public List<string> FirstNames { get; set; }
2424
}
2525

26+
public class MockPerson
27+
{
28+
public string Name { get; set; }
29+
public MockListOfPhone Phones { get; set; }
30+
}
31+
32+
public class MockPhone
33+
{
34+
public string Number { get; set; }
35+
}
36+
37+
public class MockListOfPhone : List<MockPhone>
38+
{
39+
40+
}
41+
2642
[TestClass]
2743
public class SelectTests
2844
{
45+
[TestMethod]
46+
public void TestSelectWithInheritedList()
47+
{
48+
var list = new List<MockPerson>()
49+
{
50+
new MockPerson
51+
{
52+
Name = "David Lebee",
53+
Phones = new MockListOfPhone
54+
{
55+
new MockPhone
56+
{
57+
Number = "0000000000"
58+
}
59+
}
60+
},
61+
new MockPerson
62+
{
63+
Name = "Yubing Liang",
64+
Phones = new MockListOfPhone
65+
{
66+
new MockPhone
67+
{
68+
Number = "1111111111"
69+
}
70+
}
71+
}
72+
};
73+
74+
var names = list.AsQueryable()
75+
.Where(t => t.Equal("Phones.Number", "1111111111"))
76+
.Select(t =>
77+
{
78+
t.Path("Name");
79+
t.FirstOrDefault("Phones.Number", "Number", SelectCollectionHandling.Flatten);
80+
})
81+
.ToDynamicClassList();
82+
83+
Assert.IsTrue(names.Count() == 1);
84+
var firstPerson = names.First();
85+
Assert.AreEqual("Yubing Liang", firstPerson.GetDynamicPropertyValue<string>("Name"));
86+
Assert.AreEqual("1111111111", firstPerson.GetDynamicPropertyValue<string>("Number"));
87+
}
88+
2989
[TestMethod]
3090
public void TestSelect()
3191
{

PoweredSoft.DynamicLinq.Test/SimpleQueriesTest.cs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,9 @@ public void TestingSort()
135135
// subject.
136136
var posts = new List<Post>()
137137
{
138-
new Post { Id = 1, AuthorId = 1, Title = "Hello 1", Content = "World" },
139-
new Post { Id = 2, AuthorId = 1, Title = "Hello 2", Content = "World" },
140-
new Post { Id = 3, AuthorId = 2, Title = "Hello 3", Content = "World" },
138+
new Post { Id = 1, AuthorId = 1, Title = "Hello 1", Content = "World"},
139+
new Post { Id = 2, AuthorId = 1, Title = "Hello 2", Content = "World"},
140+
new Post { Id = 3, AuthorId = 2, Title = "Hello 3", Content = "World"},
141141
};
142142

143143
// the query.
@@ -151,5 +151,24 @@ public void TestingSort()
151151
Assert.IsTrue(first.Id == 3);
152152
Assert.IsTrue(second.Id == 1);
153153
}
154+
155+
[TestMethod]
156+
public void TestingSort2()
157+
{
158+
// subject.
159+
var posts = new List<Post>()
160+
{
161+
new Post { Id = 1, AuthorId = 1, Title = "Hello 1", Content = "World", Comments = new List<Comment> { } },
162+
new Post { Id = 2, AuthorId = 1, Title = "Hello 2", Content = "World", Comments = new List<Comment> { } },
163+
new Post { Id = 3, AuthorId = 2, Title = "Hello 3", Content = "World", Comments = new List<Comment> { } },
164+
};
165+
166+
// the query.
167+
var query = posts.AsQueryable();
168+
169+
// just testing that the expressionm can be created, some drivers support seleting in collections.
170+
var query2 = query.OrderByDescending("Comments");
171+
var query3 = query.OrderByDescending("Comments.PostId");
172+
}
154173
}
155174
}

PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ private static Expression WrapIntoSelectFromGrouping(ParameterExpression paramet
253253

254254
Expression selectExpression;
255255
if (QueryableHelpers.IsGenericEnumerable(innerSelectType) && selectCollectionHandling == SelectCollectionHandling.Flatten)
256-
selectExpression = Expression.Call(typeof(Enumerable), "SelectMany", new Type[] { selectType, innerSelectType.GenericTypeArguments.First() }, parameter, innerLambdaExpression);
256+
selectExpression = Expression.Call(typeof(Enumerable), "SelectMany", new Type[] { selectType, QueryableHelpers.GetTypeOfEnumerable(innerSelectType, true) }, parameter, innerLambdaExpression);
257257
else
258258
selectExpression = Expression.Call(typeof(Enumerable), "Select", new Type[] { selectType, innerSelectType }, parameter, innerLambdaExpression);
259259

@@ -359,7 +359,7 @@ internal static Expression InternalResolvePathExpression(int step, Expression pa
359359
else
360360
{
361361
// enumerable.
362-
var listGenericArgumentType = memberExpression.Type.GetGenericArguments().First();
362+
var listGenericArgumentType = QueryableHelpers.GetTypeOfEnumerable(memberExpression.Type, true);
363363

364364
// sub param.
365365
var innerParam = Expression.Parameter(listGenericArgumentType);
@@ -432,7 +432,7 @@ public static ConstantExpression ResolveConstant(Expression member, object value
432432
public static IQueryable CreateOrderByExpression(IQueryable query, string path, QueryOrderByDirection direction, bool append = true)
433433
{
434434
var parameter = Expression.Parameter(query.ElementType, "t");
435-
var member = QueryableHelpers.ResolvePathForExpression(parameter, path);
435+
var member = QueryableHelpers.ResolvePathForExpression(parameter, path, false);
436436

437437
string sortCommand = null;
438438
if (direction == QueryOrderByDirection.Descending)
@@ -503,7 +503,7 @@ internal static Expression InternalCreateConditionExpression(int recursionStep,
503503

504504
if (IsGenericEnumerable(memberExpression))
505505
{
506-
var listGenericArgumentType = memberExpression.Type.GetGenericArguments().First();
506+
var listGenericArgumentType = QueryableHelpers.GetTypeOfEnumerable(memberExpression.Type, true);
507507
var innerParameter = Expression.Parameter(listGenericArgumentType, $"t{++recursionStep}");
508508
var innerLambda = InternalCreateConditionExpression(recursionStep, listGenericArgumentType, innerParameter, innerParameter, parts.Skip(1).ToList(), condition, value, convertStrategy, collectionHandling, nullChecking, stringComparison, negate);
509509

@@ -623,12 +623,43 @@ public static Expression CreateConditionExpression(Type type,
623623
public static bool IsGenericEnumerable(Expression member) => IsGenericEnumerable(member.Type);
624624
public static bool IsGenericEnumerable(Type type)
625625
{
626-
if (!type.IsGenericType)
626+
if (type == typeof(string))
627627
return false;
628628

629-
var genericArgumentType = type.GenericTypeArguments.First();
630-
var ret = typeof(IEnumerable<>).MakeGenericType(genericArgumentType).IsAssignableFrom(type);
631-
return ret;
629+
if (type.IsGenericType)
630+
{
631+
var makeGenericType = typeof(IEnumerable<>).MakeGenericType(type.GetGenericArguments()[0]);
632+
var possible = makeGenericType.IsAssignableFrom(type);
633+
if (possible)
634+
return true;
635+
}
636+
637+
var result = type.GetInterfaces().Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>));
638+
return result;
639+
}
640+
641+
public static Type GetTypeOfEnumerable(Type genericEnumerableType, bool throwIfNotEnumerable)
642+
{
643+
Type result = null;
644+
645+
if (genericEnumerableType.IsGenericType)
646+
{
647+
var makeGenericType = typeof(IEnumerable<>).MakeGenericType(genericEnumerableType.GetGenericArguments()[0]);
648+
var possible = makeGenericType.IsAssignableFrom(genericEnumerableType);
649+
if (possible)
650+
return genericEnumerableType.GetGenericArguments()[0];
651+
}
652+
653+
result = genericEnumerableType.GetInterfaces().FirstOrDefault(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>));
654+
if (result == null)
655+
{
656+
if (throwIfNotEnumerable)
657+
throw new Exception("Not a IEnumerable<T>");
658+
659+
return null;
660+
}
661+
662+
return result.GetGenericArguments().First();
632663
}
633664
}
634665
}

PoweredSoft.DynamicLinq/Parser/ExpressionParser.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public void Parse()
5050
{
5151
Type = memberExpression.Type,
5252
IsGenericEnumerable = QueryableHelpers.IsGenericEnumerable(memberExpression),
53-
EnumerableType = memberExpression.Type.GenericTypeArguments.FirstOrDefault(),
53+
EnumerableType = QueryableHelpers.GetTypeOfEnumerable(memberExpression.Type, false),
5454
Parent = parent,
5555
Name = pp
5656
};

PoweredSoft.DynamicLinq/Resolver/PathExpressionResolver.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,9 @@ public void Resolve()
7474
if (isSelectMany)
7575
{
7676
var selectType = parent.GroupEnumerableType();
77+
var groupExpressionEnumerableType = QueryableHelpers.GetTypeOfEnumerable(groupExpression.Type, true);
7778
var selectExpression = Expression.Call(typeof(Enumerable), "SelectMany",
78-
new Type[] { selectType, groupExpression.Type.GenericTypeArguments.First() },
79+
new Type[] { selectType, groupExpressionEnumerableType },
7980
parentExpression, groupExpressionLambda);
8081
currentExpression = selectExpression;
8182
}
@@ -109,9 +110,10 @@ public void Resolve()
109110

110111
if (isSelectMany)
111112
{
113+
var currentExpressionEnumerableType = QueryableHelpers.GetTypeOfEnumerable(currentExpression.Type, true);
112114
var currentExpressionLambda = Expression.Lambda(currentExpression, group.Parameter);
113115
currentExpression = Expression.Call(typeof(Enumerable), "SelectMany",
114-
new Type[] { selectType, currentExpression.Type.GenericTypeArguments.First() },
116+
new Type[] { selectType, currentExpressionEnumerableType },
115117
parentExpression, currentExpressionLambda);
116118
}
117119
else
@@ -144,7 +146,8 @@ private Expression CheckNullOnFirstGroup(ExpressionParserPieceGroup group, Expre
144146
Expression ifTrueExpression = null;
145147
if (QueryableHelpers.IsGenericEnumerable(nullType))
146148
{
147-
var listType = typeof(List<>).MakeGenericType(nullType.GenericTypeArguments.First());
149+
var enumerableType = QueryableHelpers.GetTypeOfEnumerable(nullType, true);
150+
var listType = typeof(List<>).MakeGenericType(enumerableType);
148151
ifTrueExpression = Expression.New(listType);
149152

150153
}

0 commit comments

Comments
 (0)