Skip to content
This repository was archived by the owner on Dec 2, 2020. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,41 @@ Okay, now select users only who have sport cars capable of speeds up to 300 km /
}
}
```
If we want to complicate the situation a little more, we can select users only who have more than 4 sport cars capable of speeds up to 300 km / hour with model Mercedes.


```csharp
var filter = new FilterContainer
{
Where = new TreeFilter
{
Field = "Cars",
FilterType = WhereFilterType.CountGreaterThan,
Value = 4,
OperandsOfCollections = new TreeFilter
{
OperatorType = TreeFilterType.And,
Operands = new List<TreeFilter>
{
new TreeFilter
{
Field = "MaxSpeed",
FilterType = WhereFilterType.GreaterThan,
Value = 300
},
new TreeFilter
{
Field = "Model",
FilterType = WhereFilterType.Equals,
Value = "Mercedes"
}
}

}
},
};
```

Field supports the appeal to the properties of the members of the entity with unlimited nesting. Similarly works sorting.

## Filtering methods
Expand All @@ -185,6 +220,11 @@ Currently FilterType allows you to filter by the following ways:
2. Applied to the listed items without using Value:
* Any
* NotAny
* CountEquals
* CountLessThan
* CountGreaterThan
* CountLessThanOrEqual
* CountGreaterThanOrEqual

## Entity members
Available types for single member entities, **which can be filtered**:
Expand Down
139 changes: 129 additions & 10 deletions src/QueryDesignerCore/Expressions/WhereExpression.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
Expand All @@ -15,6 +16,12 @@ internal static class WhereExpression
/// </summary>
private static readonly Type StringType = typeof(string);


/// <summary>
/// Integer type.
/// </summary>
private static readonly Type IntType = typeof(int);

/// <summary>
/// Expression type.
/// </summary>
Expand Down Expand Up @@ -71,11 +78,45 @@ internal static class WhereExpression
method => method.Name == "Any" && method.IsStatic &&
method.GetParameters().Length == 2);

/// <summary>
/// Info about "Any" method with one parameter for collection.
/// </summary>
private static readonly MethodInfo CollectionCount = QueryableType.GetRuntimeMethods().Single(
method => method.Name == "Count" &&
method.GetParameters().Length == 1);

/// <summary>
/// Info about "Any" method with one parameter for collection.
/// </summary>
private static readonly MethodInfo CollectionCount2 = QueryableType.GetRuntimeMethods().Single(
method => method.Name == "Count" &&
method.GetParameters().Length == 2);

/// <summary>
/// Info about method of constructing expressions.
/// </summary>
private static readonly MethodInfo ExpressionMethod = typeof(WhereExpression).GetRuntimeMethods().FirstOrDefault(m => m.Name == "GetExpression");

/// <summary>
/// Info about method of constructing expressions.
/// </summary>
private static readonly MethodInfo ExpressionTreeMethod = typeof(WhereExpression).GetRuntimeMethods().FirstOrDefault(m => m.Name == "GetTreeExpression");


/// <summary>
/// Info about avaliable methods for filter types.
/// </summary>
private static readonly Dictionary<WhereFilterType, List<MethodInfo>> FilterTypeAvaliableMethods = new Dictionary<WhereFilterType, List<MethodInfo>>()
{
{WhereFilterType.Any, new List<MethodInfo>(){CollectionAny,CollectionAny2 } },
{WhereFilterType.NotAny, new List<MethodInfo>(){CollectionAny,CollectionAny2 } },
{WhereFilterType.CountEquals, new List<MethodInfo>(){ CollectionCount, CollectionCount2 } },
{WhereFilterType.CountGreaterThan, new List<MethodInfo>(){ CollectionCount, CollectionCount2 } },
{WhereFilterType.CountGreaterThanOrEqual, new List<MethodInfo>(){ CollectionCount, CollectionCount2 } },
{WhereFilterType.CountLessThan, new List<MethodInfo>(){ CollectionCount, CollectionCount2 } },
{WhereFilterType.CountLessThanOrEqual, new List<MethodInfo>(){ CollectionCount, CollectionCount2 } },
};

/// <summary>
/// Available types for conversion.
/// </summary>
Expand Down Expand Up @@ -126,6 +167,8 @@ internal static class WhereExpression
public static Expression<Func<T, bool>> GetExpression<T>(this WhereFilter filter, string suffix = "")
{
var e = Expression.Parameter(typeof(T), "e" + suffix);


var exs = GetExpressionForField(e, filter, suffix + "0");
return Expression.Lambda<Func<T, bool>>(exs, e);
}
Expand Down Expand Up @@ -202,22 +245,26 @@ private static Expression GetExpressionForField(Expression e, WhereFilter filter
Field = t,
FilterType = filter.FilterType,
Value = filter.Value
},
suffix
}, suffix
};
var expr = (Expression)generic.Invoke(null, pars);
return Expression.Call(

var call = Expression.Call(
CollectionAny2.MakeGenericMethod(
prop.Type.GenericTypeArguments.First()),
prop,
expr);

return call;

}
prop = Expression.Property(prop, GetDeclaringProperty(prop, t));
}
var exp = GenerateExpressionOneField(prop, filter);
return exp;
}


/// <summary>
/// Construct bool-expression between different expression and a filter.
/// </summary>
Expand Down Expand Up @@ -282,16 +329,13 @@ private static Expression GenerateExpressionOneField(Expression prop, WhereFilte
case WhereFilterType.Any:
if (IsEnumerable(prop))
prop = AsQueryable(prop);
var ca = CollectionAny.MakeGenericMethod(
prop.Type.GenericTypeArguments.First());
return Expression.Call(ca, prop);

return CreateMethodCall(prop, filter);

case WhereFilterType.NotAny:
if (IsEnumerable(prop))
prop = AsQueryable(prop);
var cna = CollectionAny.MakeGenericMethod(
prop.Type.GenericTypeArguments.First());
return Expression.Not(Expression.Call(cna, prop));
return Expression.Not(CreateMethodCall(prop, filter));

case WhereFilterType.IsNull:
return Expression.Equal(prop, ToStaticParameterExpressionOfType(null, prop.Type));
Expand Down Expand Up @@ -326,10 +370,85 @@ private static Expression GenerateExpressionOneField(Expression prop, WhereFilte
Expression.Equal(prop, ToStaticParameterExpressionOfType(null, prop.Type)),
Expression.Equal(prop, ToStaticParameterExpressionOfType(string.Empty, prop.Type))));


case WhereFilterType.CountEquals:
if (IsEnumerable(prop))
prop = AsQueryable(prop);

return Expression.Equal(CreateMethodCall(prop, filter), Expression.Constant(filter.Value, IntType));

case WhereFilterType.CountGreaterThan:
if (IsEnumerable(prop))
prop = AsQueryable(prop);

return Expression.GreaterThan(CreateMethodCall(prop, filter), Expression.Constant(filter.Value, IntType));

case WhereFilterType.CountGreaterThanOrEqual:
if (IsEnumerable(prop))
prop = AsQueryable(prop);
return Expression.GreaterThanOrEqual(CreateMethodCall(prop, filter), Expression.Constant(filter.Value, IntType));

case WhereFilterType.CountLessThan:
if (IsEnumerable(prop))
prop = AsQueryable(prop);
return Expression.LessThan(CreateMethodCall(prop, filter), Expression.Constant(filter.Value, IntType));

case WhereFilterType.CountLessThanOrEqual:
if (IsEnumerable(prop))
prop = AsQueryable(prop);
return Expression.LessThanOrEqual(CreateMethodCall(prop, filter), Expression.Constant(filter.Value, IntType));

default:
return prop;
}
}
/// <summary>
/// Create method call with different expression
/// </summary>
/// <param name="prop">Different expression.</param>
/// <param name="filter">Filter for query.</param>
/// <returns>Method call expression.</returns>
private static MethodCallExpression CreateMethodCall(Expression prop, WhereFilter filter)
{
var methods = FilterTypeAvaliableMethods[filter.FilterType];


var mf = methods[0].MakeGenericMethod(
prop.Type.GenericTypeArguments.First());

var mf2 = methods[1].MakeGenericMethod(
prop.Type.GenericTypeArguments.First());


return filter.OperandsOfCollections == null ?
Expression.Call(mf, prop) :
Expression.Call(mf2, prop, CreateCollectionMethodExpression(filter.OperandsOfCollections, prop.Type.GenericTypeArguments.First()));

}


/// <summary>
/// Create expression for collection methods
/// </summary>
/// <param name="filter">Filter value.</param>
/// <param name="type">Conversion to type.</param>
/// <param name="suffix">Suffix vor variable.</param>
/// <returns>Converted value.</returns>
private static Expression CreateCollectionMethodExpression(TreeFilter filter, Type type, string suffix = "")
{

var generic = ExpressionTreeMethod.MakeGenericMethod(type);

object[] pars = {
filter
,suffix+"0"
};
var expr = (Expression)generic.Invoke(null, pars);

return expr;

}


/// <summary>
/// Value type filter field conversion.
Expand Down Expand Up @@ -383,7 +502,7 @@ private static object TryCastFieldValueType(object value, Type type)
private static Expression ToStaticParameterExpressionOfType(object obj, Type type)
=> Expression.Convert(
Expression.Property(
Expression.Constant(new { obj }),
Expression.Constant(new { obj }),
"obj"),
type);

Expand Down
6 changes: 6 additions & 0 deletions src/QueryDesignerCore/WhereFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,11 @@ public WhereFilter()
/// Value for filtering.
/// </summary>
public object Value { get; set; }

/// <summary>
/// Operands of collection boolean expressions.
/// </summary>
public TreeFilter OperandsOfCollections { get; set; }

}
}
34 changes: 33 additions & 1 deletion src/QueryDesignerCore/WhereFilterType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,38 @@ public enum WhereFilterType
/// <summary>
/// The field is not null or empty
/// </summary>
IsNotNullOrEmpty
IsNotNullOrEmpty,


/// <summary>
/// Collection count equal the value.
/// </summary>
CountEquals,


/// <summary>
/// Collection count less than the value.
/// </summary>
CountLessThan,

/// <summary>
/// Collection count is less than or equal to value.
/// </summary>
CountLessThanOrEqual,

/// <summary>
/// Collection count greater than the value.
/// </summary>
CountGreaterThan,



/// <summary>
/// Collection count is greater than or equal to the value.
/// </summary>
CountGreaterThanOrEqual,



}
}