ready for pull-request.

This commit is contained in:
David Lebee 2020-07-31 13:02:51 -04:00
parent 7d9755ef58
commit a0c59a613a
6 changed files with 147 additions and 15 deletions

View File

@ -7,10 +7,29 @@ using PoweredSoft.DynamicLinq.Helpers;
namespace PoweredSoft.DynamicLinq.Test namespace PoweredSoft.DynamicLinq.Test
{ {
class Foo
{
}
class ListOfFoo : List<Foo>
{
}
[TestClass] [TestClass]
public class HelpersTests public class HelpersTests
{ {
[TestMethod]
public void TestInheritanceOfListAsGenericEnumerableType()
{
var shouldBeTrue = QueryableHelpers.IsGenericEnumerable(typeof(ListOfFoo));
Assert.IsTrue(shouldBeTrue);
var type = QueryableHelpers.GetTypeOfEnumerable(typeof(ListOfFoo), true);
Assert.IsTrue(type == typeof(Foo));
}
[TestMethod] [TestMethod]
public void TestCreateFilterExpression() public void TestCreateFilterExpression()
{ {

View File

@ -23,9 +23,69 @@ namespace PoweredSoft.DynamicLinq.Test
public List<string> FirstNames { get; set; } public List<string> FirstNames { get; set; }
} }
public class MockPerson
{
public string Name { get; set; }
public MockListOfPhone Phones { get; set; }
}
public class MockPhone
{
public string Number { get; set; }
}
public class MockListOfPhone : List<MockPhone>
{
}
[TestClass] [TestClass]
public class SelectTests public class SelectTests
{ {
[TestMethod]
public void TestSelectWithInheritedList()
{
var list = new List<MockPerson>()
{
new MockPerson
{
Name = "David Lebee",
Phones = new MockListOfPhone
{
new MockPhone
{
Number = "0000000000"
}
}
},
new MockPerson
{
Name = "Yubing Liang",
Phones = new MockListOfPhone
{
new MockPhone
{
Number = "1111111111"
}
}
}
};
var names = list.AsQueryable()
.Where(t => t.Equal("Phones.Number", "1111111111"))
.Select(t =>
{
t.Path("Name");
t.FirstOrDefault("Phones.Number", "Number", SelectCollectionHandling.Flatten);
})
.ToDynamicClassList();
Assert.IsTrue(names.Count() == 1);
var firstPerson = names.First();
Assert.AreEqual("Yubing Liang", firstPerson.GetDynamicPropertyValue<string>("Name"));
Assert.AreEqual("1111111111", firstPerson.GetDynamicPropertyValue<string>("Number"));
}
[TestMethod] [TestMethod]
public void TestSelect() public void TestSelect()
{ {

View File

@ -135,9 +135,9 @@ namespace PoweredSoft.DynamicLinq.Test
// subject. // subject.
var posts = new List<Post>() var posts = new List<Post>()
{ {
new Post { Id = 1, AuthorId = 1, Title = "Hello 1", Content = "World" }, new Post { Id = 1, AuthorId = 1, Title = "Hello 1", Content = "World"},
new Post { Id = 2, AuthorId = 1, Title = "Hello 2", Content = "World" }, new Post { Id = 2, AuthorId = 1, Title = "Hello 2", Content = "World"},
new Post { Id = 3, AuthorId = 2, Title = "Hello 3", Content = "World" }, new Post { Id = 3, AuthorId = 2, Title = "Hello 3", Content = "World"},
}; };
// the query. // the query.
@ -151,5 +151,24 @@ namespace PoweredSoft.DynamicLinq.Test
Assert.IsTrue(first.Id == 3); Assert.IsTrue(first.Id == 3);
Assert.IsTrue(second.Id == 1); Assert.IsTrue(second.Id == 1);
} }
[TestMethod]
public void TestingSort2()
{
// subject.
var posts = new List<Post>()
{
new Post { Id = 1, AuthorId = 1, Title = "Hello 1", Content = "World", Comments = new List<Comment> { } },
new Post { Id = 2, AuthorId = 1, Title = "Hello 2", Content = "World", Comments = new List<Comment> { } },
new Post { Id = 3, AuthorId = 2, Title = "Hello 3", Content = "World", Comments = new List<Comment> { } },
};
// the query.
var query = posts.AsQueryable();
// just testing that the expressionm can be created, some drivers support seleting in collections.
var query2 = query.OrderByDescending("Comments");
var query3 = query.OrderByDescending("Comments.PostId");
}
} }
} }

View File

@ -253,7 +253,7 @@ namespace PoweredSoft.DynamicLinq.Helpers
Expression selectExpression; Expression selectExpression;
if (QueryableHelpers.IsGenericEnumerable(innerSelectType) && selectCollectionHandling == SelectCollectionHandling.Flatten) if (QueryableHelpers.IsGenericEnumerable(innerSelectType) && selectCollectionHandling == SelectCollectionHandling.Flatten)
selectExpression = Expression.Call(typeof(Enumerable), "SelectMany", new Type[] { selectType, innerSelectType.GenericTypeArguments.First() }, parameter, innerLambdaExpression); selectExpression = Expression.Call(typeof(Enumerable), "SelectMany", new Type[] { selectType, QueryableHelpers.GetTypeOfEnumerable(innerSelectType, true) }, parameter, innerLambdaExpression);
else else
selectExpression = Expression.Call(typeof(Enumerable), "Select", new Type[] { selectType, innerSelectType }, parameter, innerLambdaExpression); selectExpression = Expression.Call(typeof(Enumerable), "Select", new Type[] { selectType, innerSelectType }, parameter, innerLambdaExpression);
@ -359,7 +359,7 @@ namespace PoweredSoft.DynamicLinq.Helpers
else else
{ {
// enumerable. // enumerable.
var listGenericArgumentType = memberExpression.Type.GetGenericArguments().First(); var listGenericArgumentType = QueryableHelpers.GetTypeOfEnumerable(memberExpression.Type, true);
// sub param. // sub param.
var innerParam = Expression.Parameter(listGenericArgumentType); var innerParam = Expression.Parameter(listGenericArgumentType);
@ -432,7 +432,7 @@ namespace PoweredSoft.DynamicLinq.Helpers
public static IQueryable CreateOrderByExpression(IQueryable query, string path, QueryOrderByDirection direction, bool append = true) public static IQueryable CreateOrderByExpression(IQueryable query, string path, QueryOrderByDirection direction, bool append = true)
{ {
var parameter = Expression.Parameter(query.ElementType, "t"); var parameter = Expression.Parameter(query.ElementType, "t");
var member = QueryableHelpers.ResolvePathForExpression(parameter, path); var member = QueryableHelpers.ResolvePathForExpression(parameter, path, false);
string sortCommand = null; string sortCommand = null;
if (direction == QueryOrderByDirection.Descending) if (direction == QueryOrderByDirection.Descending)
@ -503,7 +503,7 @@ namespace PoweredSoft.DynamicLinq.Helpers
if (IsGenericEnumerable(memberExpression)) if (IsGenericEnumerable(memberExpression))
{ {
var listGenericArgumentType = memberExpression.Type.GetGenericArguments().First(); var listGenericArgumentType = QueryableHelpers.GetTypeOfEnumerable(memberExpression.Type, true);
var innerParameter = Expression.Parameter(listGenericArgumentType, $"t{++recursionStep}"); var innerParameter = Expression.Parameter(listGenericArgumentType, $"t{++recursionStep}");
var innerLambda = InternalCreateConditionExpression(recursionStep, listGenericArgumentType, innerParameter, innerParameter, parts.Skip(1).ToList(), condition, value, convertStrategy, collectionHandling, nullChecking, stringComparison, negate); var innerLambda = InternalCreateConditionExpression(recursionStep, listGenericArgumentType, innerParameter, innerParameter, parts.Skip(1).ToList(), condition, value, convertStrategy, collectionHandling, nullChecking, stringComparison, negate);
@ -623,12 +623,43 @@ namespace PoweredSoft.DynamicLinq.Helpers
public static bool IsGenericEnumerable(Expression member) => IsGenericEnumerable(member.Type); public static bool IsGenericEnumerable(Expression member) => IsGenericEnumerable(member.Type);
public static bool IsGenericEnumerable(Type type) public static bool IsGenericEnumerable(Type type)
{ {
if (!type.IsGenericType) if (type == typeof(string))
return false; return false;
var genericArgumentType = type.GenericTypeArguments.First(); if (type.IsGenericType)
var ret = typeof(IEnumerable<>).MakeGenericType(genericArgumentType).IsAssignableFrom(type); {
return ret; var makeGenericType = typeof(IEnumerable<>).MakeGenericType(type.GetGenericArguments()[0]);
var possible = makeGenericType.IsAssignableFrom(type);
if (possible)
return true;
}
var result = type.GetInterfaces().Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>));
return result;
}
public static Type GetTypeOfEnumerable(Type genericEnumerableType, bool throwIfNotEnumerable)
{
Type result = null;
if (genericEnumerableType.IsGenericType)
{
var makeGenericType = typeof(IEnumerable<>).MakeGenericType(genericEnumerableType.GetGenericArguments()[0]);
var possible = makeGenericType.IsAssignableFrom(genericEnumerableType);
if (possible)
return genericEnumerableType.GetGenericArguments()[0];
}
result = genericEnumerableType.GetInterfaces().FirstOrDefault(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>));
if (result == null)
{
if (throwIfNotEnumerable)
throw new Exception("Not a IEnumerable<T>");
return null;
}
return result.GetGenericArguments().First();
} }
} }
} }

View File

@ -50,7 +50,7 @@ namespace PoweredSoft.DynamicLinq.Parser
{ {
Type = memberExpression.Type, Type = memberExpression.Type,
IsGenericEnumerable = QueryableHelpers.IsGenericEnumerable(memberExpression), IsGenericEnumerable = QueryableHelpers.IsGenericEnumerable(memberExpression),
EnumerableType = memberExpression.Type.GenericTypeArguments.FirstOrDefault(), EnumerableType = QueryableHelpers.GetTypeOfEnumerable(memberExpression.Type, false),
Parent = parent, Parent = parent,
Name = pp Name = pp
}; };

View File

@ -74,8 +74,9 @@ namespace PoweredSoft.DynamicLinq.Resolver
if (isSelectMany) if (isSelectMany)
{ {
var selectType = parent.GroupEnumerableType(); var selectType = parent.GroupEnumerableType();
var groupExpressionEnumerableType = QueryableHelpers.GetTypeOfEnumerable(groupExpression.Type, true);
var selectExpression = Expression.Call(typeof(Enumerable), "SelectMany", var selectExpression = Expression.Call(typeof(Enumerable), "SelectMany",
new Type[] { selectType, groupExpression.Type.GenericTypeArguments.First() }, new Type[] { selectType, groupExpressionEnumerableType },
parentExpression, groupExpressionLambda); parentExpression, groupExpressionLambda);
currentExpression = selectExpression; currentExpression = selectExpression;
} }
@ -109,9 +110,10 @@ namespace PoweredSoft.DynamicLinq.Resolver
if (isSelectMany) if (isSelectMany)
{ {
var currentExpressionEnumerableType = QueryableHelpers.GetTypeOfEnumerable(currentExpression.Type, true);
var currentExpressionLambda = Expression.Lambda(currentExpression, group.Parameter); var currentExpressionLambda = Expression.Lambda(currentExpression, group.Parameter);
currentExpression = Expression.Call(typeof(Enumerable), "SelectMany", currentExpression = Expression.Call(typeof(Enumerable), "SelectMany",
new Type[] { selectType, currentExpression.Type.GenericTypeArguments.First() }, new Type[] { selectType, currentExpressionEnumerableType },
parentExpression, currentExpressionLambda); parentExpression, currentExpressionLambda);
} }
else else
@ -144,7 +146,8 @@ namespace PoweredSoft.DynamicLinq.Resolver
Expression ifTrueExpression = null; Expression ifTrueExpression = null;
if (QueryableHelpers.IsGenericEnumerable(nullType)) if (QueryableHelpers.IsGenericEnumerable(nullType))
{ {
var listType = typeof(List<>).MakeGenericType(nullType.GenericTypeArguments.First()); var enumerableType = QueryableHelpers.GetTypeOfEnumerable(nullType, true);
var listType = typeof(List<>).MakeGenericType(enumerableType);
ifTrueExpression = Expression.New(listType); ifTrueExpression = Expression.New(listType);
} }