collection handling.
and auto null checking if enabled.
This commit is contained in:
		
							parent
							
								
									babce13871
								
							
						
					
					
						commit
						e3660877ef
					
				| @ -99,6 +99,81 @@ namespace PoweredSoft.DynamicLinq.Test | |||||||
|             Assert.IsTrue(second.Id == 1); |             Assert.IsTrue(second.Id == 1); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         [TestMethod] | ||||||
|  |         public void TestCreateFilterExpressionCheckNull() | ||||||
|  |         { | ||||||
|  |             var authors = new List<Author>() | ||||||
|  |             { | ||||||
|  |                 new Author | ||||||
|  |                 { | ||||||
|  |                     Id = 1, | ||||||
|  |                     FirstName = "David", | ||||||
|  |                     LastName = "Lebee", | ||||||
|  |                     Posts = new List<Post> | ||||||
|  |                     { | ||||||
|  |                         new Post | ||||||
|  |                         { | ||||||
|  |                             Id = 1, | ||||||
|  |                             AuthorId = 1, | ||||||
|  |                             Title = "Match", | ||||||
|  |                             Content = "ABC", | ||||||
|  |                             Comments = new List<Comment>() | ||||||
|  |                             { | ||||||
|  |                                 new Comment() | ||||||
|  |                                 { | ||||||
|  |                                     Id = 1, | ||||||
|  |                                     DisplayName = "John Doe", | ||||||
|  |                                     CommentText = "!@#$!@#!@#", | ||||||
|  |                                     Email = "john.doe@me.com" | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                         }, | ||||||
|  |                         new Post | ||||||
|  |                         { | ||||||
|  |                             Id = 2, | ||||||
|  |                             AuthorId = 1, | ||||||
|  |                             Title = "Match", | ||||||
|  |                             Content = "ABC" | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 }, | ||||||
|  |                 new Author | ||||||
|  |                 { | ||||||
|  |                     Id = 2, | ||||||
|  |                     FirstName = "Chuck", | ||||||
|  |                     LastName = "Norris", | ||||||
|  |                     Posts = new List<Post> | ||||||
|  |                     { | ||||||
|  |                         new Post | ||||||
|  |                         { | ||||||
|  |                             Id = 3, | ||||||
|  |                             AuthorId = 2, | ||||||
|  |                             Title = "Match", | ||||||
|  |                             Content = "ASD" | ||||||
|  |                         }, | ||||||
|  |                         new Post | ||||||
|  |                         { | ||||||
|  |                             Id = 4, | ||||||
|  |                             AuthorId = 2, | ||||||
|  |                             Title = "DontMatch", | ||||||
|  |                             Content = "ASD" | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             // the query. | ||||||
|  |             var query = authors.AsQueryable(); | ||||||
|  | 
 | ||||||
|  |             query = query.Query(qb => | ||||||
|  |             { | ||||||
|  |                 qb.NullChecking(); | ||||||
|  |                 qb.And("Posts.Comments.Email", ConditionOperators.Equal, "john.doe@me.com", collectionHandling: QueryCollectionHandling.Any); | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             Assert.AreEqual(1, query.Count()); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         [TestMethod] |         [TestMethod] | ||||||
|         public void TestCreateFilterExpression() |         public void TestCreateFilterExpression() | ||||||
|         { |         { | ||||||
| @ -133,7 +208,8 @@ namespace PoweredSoft.DynamicLinq.Test | |||||||
|                             Id = 2, |                             Id = 2, | ||||||
|                             AuthorId = 1, |                             AuthorId = 1, | ||||||
|                             Title = "Match", |                             Title = "Match", | ||||||
|                             Content = "ABC" |                             Content = "ABC", | ||||||
|  |                             Comments = new List<Comment>() | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 }, |                 }, | ||||||
| @ -150,6 +226,7 @@ namespace PoweredSoft.DynamicLinq.Test | |||||||
|                             AuthorId = 2, |                             AuthorId = 2, | ||||||
|                             Title = "Match", |                             Title = "Match", | ||||||
|                             Content = "ASD", |                             Content = "ASD", | ||||||
|  |                             Comments = new List<Comment>() | ||||||
|                         }, |                         }, | ||||||
|                         new Post |                         new Post | ||||||
|                         { |                         { | ||||||
| @ -157,6 +234,7 @@ namespace PoweredSoft.DynamicLinq.Test | |||||||
|                             AuthorId = 2, |                             AuthorId = 2, | ||||||
|                             Title = "DontMatch", |                             Title = "DontMatch", | ||||||
|                             Content = "ASD", |                             Content = "ASD", | ||||||
|  |                             Comments = new List<Comment>() | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
| @ -165,10 +243,12 @@ namespace PoweredSoft.DynamicLinq.Test | |||||||
|             // the query. |             // the query. | ||||||
|             var query = authors.AsQueryable(); |             var query = authors.AsQueryable(); | ||||||
| 
 | 
 | ||||||
|             var allExpression = QueryableHelpers.CreateFilterExpression<Author>("Posts.Title", ConditionOperators.Equal, "Match", QueryConvertStrategy.ConvertConstantToComparedPropertyOrField, QueryCollectionCondition.All); |             var allExpression = QueryableHelpers.CreateFilterExpression<Author>("Posts.Title", ConditionOperators.Equal, "Match", QueryConvertStrategy.ConvertConstantToComparedPropertyOrField, QueryCollectionHandling.All); | ||||||
|             var anyExpression = QueryableHelpers.CreateFilterExpression<Author>("Posts.Title", ConditionOperators.Equal, "Match", QueryConvertStrategy.ConvertConstantToComparedPropertyOrField, QueryCollectionCondition.Any); |             var anyExpression = QueryableHelpers.CreateFilterExpression<Author>("Posts.Title", ConditionOperators.Equal, "Match", QueryConvertStrategy.ConvertConstantToComparedPropertyOrField, QueryCollectionHandling.Any); | ||||||
|  |             var anyExpression2 = QueryableHelpers.CreateFilterExpression<Author>("Posts.Comments.Email", ConditionOperators.Equal, "John.doe@me.com", QueryConvertStrategy.ConvertConstantToComparedPropertyOrField, QueryCollectionHandling.Any); | ||||||
|             Assert.AreEqual(1, query.Count(allExpression)); |             Assert.AreEqual(1, query.Count(allExpression)); | ||||||
|             Assert.AreEqual(2, query.Count(anyExpression)); |             Assert.AreEqual(2, query.Count(anyExpression)); | ||||||
|  |             Assert.AreEqual(1, query.Count(anyExpression2)); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ namespace PoweredSoft.DynamicLinq | |||||||
|     public enum ConditionOperators  |     public enum ConditionOperators  | ||||||
|     { |     { | ||||||
|         Equal, |         Equal, | ||||||
|  |         NotEqual, | ||||||
|         GreaterThan, |         GreaterThan, | ||||||
|         GreaterThanOrEqual, |         GreaterThanOrEqual, | ||||||
|         LessThan, |         LessThan, | ||||||
| @ -25,7 +26,7 @@ namespace PoweredSoft.DynamicLinq | |||||||
|         ConvertConstantToComparedPropertyOrField |         ConvertConstantToComparedPropertyOrField | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public enum QueryCollectionCondition |     public enum QueryCollectionHandling | ||||||
|     { |     { | ||||||
|         Any, |         Any, | ||||||
|         All |         All | ||||||
|  | |||||||
| @ -15,6 +15,8 @@ namespace PoweredSoft.DynamicLinq.Fluent | |||||||
| 
 | 
 | ||||||
|         public Type QueryableType { get; set; } |         public Type QueryableType { get; set; } | ||||||
| 
 | 
 | ||||||
|  |         public bool IsNullCheckingEnabled { get; protected set; } = false; | ||||||
|  | 
 | ||||||
|         public List<QueryBuilderFilter> Filters { get; protected set; } = new List<QueryBuilderFilter>(); |         public List<QueryBuilderFilter> Filters { get; protected set; } = new List<QueryBuilderFilter>(); | ||||||
| 
 | 
 | ||||||
|         public List<QueryBuilderSort> Sorts { get; protected set; } = new List<QueryBuilderSort>(); |         public List<QueryBuilderSort> Sorts { get; protected set; } = new List<QueryBuilderSort>(); | ||||||
| @ -24,9 +26,15 @@ namespace PoweredSoft.DynamicLinq.Fluent | |||||||
|             Query = query; |             Query = query; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         public QueryBuilder<T> NullChecking(bool check = true) | ||||||
|  |         { | ||||||
|  |             IsNullCheckingEnabled = check; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         public virtual QueryBuilder<T> Compare(string path, ConditionOperators conditionOperators, object value,  |         public virtual QueryBuilder<T> Compare(string path, ConditionOperators conditionOperators, object value,  | ||||||
|             QueryConvertStrategy convertStrategy = QueryConvertStrategy.ConvertConstantToComparedPropertyOrField,  |             QueryConvertStrategy convertStrategy = QueryConvertStrategy.ConvertConstantToComparedPropertyOrField,  | ||||||
|             bool and = true) |             bool and = true, QueryCollectionHandling collectionHandling = QueryCollectionHandling.Any) | ||||||
|         { |         { | ||||||
|             Filters.Add(new QueryBuilderFilter |             Filters.Add(new QueryBuilderFilter | ||||||
|             { |             { | ||||||
| @ -34,7 +42,8 @@ namespace PoweredSoft.DynamicLinq.Fluent | |||||||
|                 ConditionOperator = conditionOperators, |                 ConditionOperator = conditionOperators, | ||||||
|                 Path = path, |                 Path = path, | ||||||
|                 Value = value, |                 Value = value, | ||||||
|                 ConvertStrategy = convertStrategy |                 ConvertStrategy = convertStrategy, | ||||||
|  |                 CollectionHandling = collectionHandling | ||||||
|             }); |             }); | ||||||
| 
 | 
 | ||||||
|             return this; |             return this; | ||||||
| @ -55,6 +64,7 @@ namespace PoweredSoft.DynamicLinq.Fluent | |||||||
|         { |         { | ||||||
|             // create query builder for same type. |             // create query builder for same type. | ||||||
|             var qb = new QueryBuilder<T>(Query); |             var qb = new QueryBuilder<T>(Query); | ||||||
|  |             qb.NullChecking(IsNullCheckingEnabled); | ||||||
| 
 | 
 | ||||||
|             // callback. |             // callback. | ||||||
|             subQuery(qb); |             subQuery(qb); | ||||||
| @ -69,11 +79,13 @@ namespace PoweredSoft.DynamicLinq.Fluent | |||||||
|             return this; |             return this; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public QueryBuilder<T> And(string path, ConditionOperators conditionOperator, object value, QueryConvertStrategy convertStrategy = QueryConvertStrategy.ConvertConstantToComparedPropertyOrField) |         public QueryBuilder<T> And(string path, ConditionOperators conditionOperator, object value,  | ||||||
|             => Compare(path, conditionOperator, value, convertStrategy: convertStrategy, and: true); |             QueryConvertStrategy convertStrategy = QueryConvertStrategy.ConvertConstantToComparedPropertyOrField, QueryCollectionHandling collectionHandling = QueryCollectionHandling.Any) | ||||||
|  |             => Compare(path, conditionOperator, value, convertStrategy: convertStrategy, collectionHandling: collectionHandling, and: true); | ||||||
| 
 | 
 | ||||||
|         public QueryBuilder<T> Or(string path, ConditionOperators conditionOperator, object value, QueryConvertStrategy convertStrategy = QueryConvertStrategy.ConvertConstantToComparedPropertyOrField) |         public QueryBuilder<T> Or(string path, ConditionOperators conditionOperator, object value,  | ||||||
|             => Compare(path, conditionOperator, value, convertStrategy: convertStrategy, and: false); |             QueryConvertStrategy convertStrategy = QueryConvertStrategy.ConvertConstantToComparedPropertyOrField, QueryCollectionHandling collectionHandling = QueryCollectionHandling.Any) | ||||||
|  |             => Compare(path, conditionOperator, value, convertStrategy: convertStrategy, collectionHandling: collectionHandling, and: false); | ||||||
| 
 | 
 | ||||||
|         public QueryBuilder<T> And(Action<QueryBuilder<T>> subQuery) |         public QueryBuilder<T> And(Action<QueryBuilder<T>> subQuery) | ||||||
|             => SubQuery(subQuery, true); |             => SubQuery(subQuery, true); | ||||||
| @ -189,9 +201,9 @@ namespace PoweredSoft.DynamicLinq.Fluent | |||||||
|                 else |                 else | ||||||
|                 { |                 { | ||||||
|                     if (filter.And) |                     if (filter.And) | ||||||
|                         temp = Expression.Lambda<Func<T, bool>>(Expression.And(temp.Body, innerExpression.Body), parameter); |                         temp = Expression.Lambda<Func<T, bool>>(Expression.AndAlso(temp.Body, innerExpression.Body), parameter); | ||||||
|                     else |                     else | ||||||
|                         temp = Expression.Lambda<Func<T, bool>>(Expression.Or(temp.Body, innerExpression.Body), parameter); |                         temp = Expression.Lambda<Func<T, bool>>(Expression.OrElse(temp.Body, innerExpression.Body), parameter); | ||||||
|                 } |                 } | ||||||
|                      |                      | ||||||
|             }); |             }); | ||||||
| @ -207,7 +219,8 @@ namespace PoweredSoft.DynamicLinq.Fluent | |||||||
|                 filter.Value, |                 filter.Value, | ||||||
|                 filter.ConvertStrategy, |                 filter.ConvertStrategy, | ||||||
|                 filter.CollectionHandling, |                 filter.CollectionHandling, | ||||||
|                 parameter: parameter |                 parameter: parameter, | ||||||
|  |                 nullChecking: IsNullCheckingEnabled | ||||||
|             ); |             ); | ||||||
| 
 | 
 | ||||||
|             return ret; |             return ret; | ||||||
|  | |||||||
| @ -14,6 +14,6 @@ namespace PoweredSoft.DynamicLinq.Fluent | |||||||
|         public bool And { get; set; } |         public bool And { get; set; } | ||||||
|         public QueryConvertStrategy ConvertStrategy { get; set; } |         public QueryConvertStrategy ConvertStrategy { get; set; } | ||||||
|         public List<QueryBuilderFilter> Filters { get; set; } = new List<QueryBuilderFilter>(); |         public List<QueryBuilderFilter> Filters { get; set; } = new List<QueryBuilderFilter>(); | ||||||
|         public QueryCollectionCondition CollectionHandling { get;  set; } |         public QueryCollectionHandling CollectionHandling { get;  set; } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -26,6 +26,8 @@ namespace PoweredSoft.DynamicLinq.Helpers | |||||||
| 
 | 
 | ||||||
|             if (conditionOperator == ConditionOperators.Equal) |             if (conditionOperator == ConditionOperators.Equal) | ||||||
|                 ret = Expression.Equal(member, constant); |                 ret = Expression.Equal(member, constant); | ||||||
|  |             else if (conditionOperator == ConditionOperators.NotEqual) | ||||||
|  |                 ret = Expression.NotEqual(member, constant); | ||||||
|             else if (conditionOperator == ConditionOperators.GreaterThan) |             else if (conditionOperator == ConditionOperators.GreaterThan) | ||||||
|                 ret = Expression.GreaterThan(member, constant); |                 ret = Expression.GreaterThan(member, constant); | ||||||
|             else if (conditionOperator == ConditionOperators.GreaterThanOrEqual) |             else if (conditionOperator == ConditionOperators.GreaterThanOrEqual) | ||||||
| @ -119,8 +121,8 @@ namespace PoweredSoft.DynamicLinq.Helpers | |||||||
|             return query; |             return query; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         internal static Expression InternalCreateFilterExpression(int recursionStep, Type type, ParameterExpression parameter, Expression current, List<string> parts,  |         internal static Expression InternalCreateFilterExpression(int recursionStep, Type type, ParameterExpression parameter, Expression current, List<string> parts, | ||||||
|             ConditionOperators condition, object value, QueryConvertStrategy convertStrategy, QueryCollectionCondition collectionHandling) |             ConditionOperators condition, object value, QueryConvertStrategy convertStrategy, QueryCollectionHandling collectionHandling, bool nullChecking) | ||||||
|         { |         { | ||||||
|             var partStr = parts.First(); |             var partStr = parts.First(); | ||||||
|             var isLast = parts.Count == 1; |             var isLast = parts.Count == 1; | ||||||
| @ -130,8 +132,9 @@ namespace PoweredSoft.DynamicLinq.Helpers | |||||||
| 
 | 
 | ||||||
|             // TODO : maybe support that last part is collection but what do we do? |             // TODO : maybe support that last part is collection but what do we do? | ||||||
|             // not supported yet. |             // not supported yet. | ||||||
|             if (isLast && IsEnumerable(memberExpression)) |             if (isLast && IsEnumerable(memberExpression) && value != null) | ||||||
|                 throw new NotSupportedException("Last part must not be a collection"); |                 throw new NotSupportedException("Can only compare collection to null"); | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|             // create the expression and return it. |             // create the expression and return it. | ||||||
|             if (isLast) |             if (isLast) | ||||||
| @ -141,30 +144,49 @@ namespace PoweredSoft.DynamicLinq.Helpers | |||||||
|                 var lambda = Expression.Lambda(filterExpression, parameter); |                 var lambda = Expression.Lambda(filterExpression, parameter); | ||||||
|                 return lambda; |                 return lambda; | ||||||
|             } |             } | ||||||
|              | 
 | ||||||
|  |             // null check. | ||||||
|  |             Expression nullCheckExpression = null; | ||||||
|  |             if (nullChecking) | ||||||
|  |                 nullCheckExpression = Expression.NotEqual(memberExpression, Expression.Constant(null)); | ||||||
|  | 
 | ||||||
|             if (IsEnumerable(memberExpression)) |             if (IsEnumerable(memberExpression)) | ||||||
|             { |             { | ||||||
|                 var listGenericArgumentType = memberExpression.Type.GetGenericArguments().First(); |                 var listGenericArgumentType = memberExpression.Type.GetGenericArguments().First(); | ||||||
|                 var innerParameter = Expression.Parameter(listGenericArgumentType, $"t{++recursionStep}"); |                 var innerParameter = Expression.Parameter(listGenericArgumentType, $"t{++recursionStep}"); | ||||||
|                 var innerLambda = InternalCreateFilterExpression(recursionStep, listGenericArgumentType, innerParameter, innerParameter, parts.Skip(1).ToList(), condition, value, convertStrategy, collectionHandling); |                 var innerLambda = InternalCreateFilterExpression(recursionStep, listGenericArgumentType, innerParameter, innerParameter, parts.Skip(1).ToList(), condition, value, convertStrategy, collectionHandling, nullChecking); | ||||||
| 
 | 
 | ||||||
|                 // the collection method. |                 // the collection method. | ||||||
|                 var collectionMethod = GetCollectionMethod(collectionHandling); |                 var collectionMethod = GetCollectionMethod(collectionHandling); | ||||||
|                 var genericMethod = collectionMethod.MakeGenericMethod(listGenericArgumentType); |                 var genericMethod = collectionMethod.MakeGenericMethod(listGenericArgumentType); | ||||||
|                 var callResult = Expression.Call(genericMethod, memberExpression, innerLambda); |                 var callResult = Expression.Call(genericMethod, memberExpression, innerLambda); | ||||||
|                 var expressionResult = Expression.Lambda(callResult, parameter); |  | ||||||
|                 return expressionResult; |  | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|             // standard property or field. |                 if (nullCheckExpression != null) | ||||||
|             return InternalCreateFilterExpression(recursionStep, type, parameter, memberExpression, parts.Skip(1).ToList(), condition, value, convertStrategy, collectionHandling); |                 { | ||||||
|  |                     var nullCheckResult = Expression.AndAlso(nullCheckExpression, callResult); | ||||||
|  |                     return Expression.Lambda(nullCheckResult, parameter); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 return Expression.Lambda(callResult, parameter); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 if (nullCheckExpression != null) | ||||||
|  |                 { | ||||||
|  |                     var pathExpr = InternalCreateFilterExpression(recursionStep, type, parameter, memberExpression, parts.Skip(1).ToList(), condition, value, convertStrategy, collectionHandling, nullChecking); | ||||||
|  |                     var nullCheckResult = Expression.AndAlso(nullCheckExpression, pathExpr); | ||||||
|  |                     return nullCheckResult; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 return InternalCreateFilterExpression(recursionStep, type, parameter, memberExpression, parts.Skip(1).ToList(), condition, value, convertStrategy, collectionHandling, nullChecking); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public static MethodInfo GetCollectionMethod(QueryCollectionCondition collectionHandling) |         public static MethodInfo GetCollectionMethod(QueryCollectionHandling collectionHandling) | ||||||
|         { |         { | ||||||
|             if (collectionHandling == QueryCollectionCondition.All) |             if (collectionHandling == QueryCollectionHandling.All) | ||||||
|                 return Constants.AllMethod; |                 return Constants.AllMethod; | ||||||
|             else if (collectionHandling == QueryCollectionCondition.Any) |             else if (collectionHandling == QueryCollectionHandling.Any) | ||||||
|                 return Constants.AnyMethod; |                 return Constants.AnyMethod; | ||||||
| 
 | 
 | ||||||
|             throw new NotSupportedException($"{collectionHandling} is not supported"); |             throw new NotSupportedException($"{collectionHandling} is not supported"); | ||||||
| @ -174,14 +196,15 @@ namespace PoweredSoft.DynamicLinq.Helpers | |||||||
|             ConditionOperators condition,  |             ConditionOperators condition,  | ||||||
|             object value,  |             object value,  | ||||||
|             QueryConvertStrategy convertStrategy,  |             QueryConvertStrategy convertStrategy,  | ||||||
|             QueryCollectionCondition collectionHandling = QueryCollectionCondition.Any, |             QueryCollectionHandling collectionHandling = QueryCollectionHandling.Any, | ||||||
|             ParameterExpression parameter = null) |             ParameterExpression parameter = null, | ||||||
|  |             bool nullChecking = false) | ||||||
|         { |         { | ||||||
|             if (parameter == null) |             if (parameter == null) | ||||||
|                 parameter = Expression.Parameter(typeof(T), "t"); |                 parameter = Expression.Parameter(typeof(T), "t"); | ||||||
| 
 | 
 | ||||||
|             var parts = path.Split('.').ToList(); |             var parts = path.Split('.').ToList(); | ||||||
|             var result = InternalCreateFilterExpression(1, typeof(T), parameter, parameter, parts, condition, value, convertStrategy, collectionHandling); |             var result = InternalCreateFilterExpression(1, typeof(T), parameter, parameter, parts, condition, value, convertStrategy, collectionHandling, nullChecking); | ||||||
|             var ret = result as Expression<Func<T, bool>>; |             var ret = result as Expression<Func<T, bool>>; | ||||||
|             return ret; |             return ret; | ||||||
|         } |         } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user