diff --git a/F23.ODataLite/F23.ODataLite.csproj b/F23.ODataLite/F23.ODataLite.csproj index 818cd15..c15fea8 100644 --- a/F23.ODataLite/F23.ODataLite.csproj +++ b/F23.ODataLite/F23.ODataLite.csproj @@ -12,7 +12,7 @@ odata asp.net core lite MIT 2019, feature[23] - 2.0.0-beta1 + 2.0.0-beta2 diff --git a/F23.ODataLite/Internal/ExpressionQueryTokenVisitor.cs b/F23.ODataLite/Internal/ExpressionQueryTokenVisitor.cs index d53a4a1..94e5d35 100644 --- a/F23.ODataLite/Internal/ExpressionQueryTokenVisitor.cs +++ b/F23.ODataLite/Internal/ExpressionQueryTokenVisitor.cs @@ -11,15 +11,18 @@ namespace F23.ODataLite.Internal internal class ExpressionQueryTokenVisitor : ISyntacticTreeVisitor { private static readonly MethodInfo _hasFlagMethod = typeof(Enum).GetMethod(nameof(Enum.HasFlag), BindingFlags.Instance | BindingFlags.Public); + private static readonly MethodInfo _toStringMethod = typeof(object).GetMethod(nameof(ToString), BindingFlags.Instance | BindingFlags.Public); private static readonly MethodInfo _fuzzyEqualsMethod = typeof(ExpressionQueryTokenVisitor).GetMethod(nameof(FuzzyEquals), BindingFlags.Static | BindingFlags.NonPublic); private readonly ParameterExpression _parameter; private readonly IEnumerable _properties; + private readonly bool _inMemoryEvaluation; - public ExpressionQueryTokenVisitor(ParameterExpression parameterExpression, IEnumerable properties) + public ExpressionQueryTokenVisitor(ParameterExpression parameterExpression, IEnumerable properties, bool inMemoryEvaluation) { _parameter = parameterExpression; _properties = properties; + _inMemoryEvaluation = inMemoryEvaluation; } private static bool FuzzyEquals(object left, object right) @@ -77,7 +80,21 @@ public Expression Visit(BinaryOperatorToken tokenIn) private Expression GetEqualsExpression(BinaryOperatorToken tokenIn) { - return Expression.Call(null, _fuzzyEqualsMethod, Expression.Convert(tokenIn.Left.Accept(this), typeof(object)), Expression.Convert(tokenIn.Right.Accept(this), typeof(object))); + if (_inMemoryEvaluation) + return Expression.Call(null, _fuzzyEqualsMethod, Expression.Convert(tokenIn.Left.Accept(this), typeof(object)), Expression.Convert(tokenIn.Right.Accept(this), typeof(object))); + + var left = tokenIn.Left.Accept(this); + var right = tokenIn.Right.Accept(this); + + return Expression.Or( + Expression.Equal(left, right), + Expression.And( + Expression.And( + Expression.NotEqual(left, Expression.Constant(null)), + Expression.NotEqual(right, Expression.Constant(null))), + Expression.Equal(Expression.Call(left, _toStringMethod), Expression.Call(right, _toStringMethod)) + ) + ); } public Expression Visit(InToken tokenIn) diff --git a/F23.ODataLite/Internal/ODataFilterOperator.cs b/F23.ODataLite/Internal/ODataFilterOperator.cs index d36097e..5d914c2 100644 --- a/F23.ODataLite/Internal/ODataFilterOperator.cs +++ b/F23.ODataLite/Internal/ODataFilterOperator.cs @@ -9,7 +9,7 @@ namespace F23.ODataLite.Internal { internal static class ODataFilterOperator { - public static IQueryable Apply(IQueryable data, IEnumerable properties, string parameter) + public static IQueryable Apply(IQueryable data, IEnumerable properties, string parameter, bool isQueryable) { var parser = new UriQueryExpressionParser(10); @@ -17,7 +17,7 @@ public static IQueryable Apply(IQueryable data, IEnumerable or IEnumerable to use ODataLite. Pass a queryable or enumerable, or remove this attribute."); + if (ok.Value is HypermediaResponse hypermedia) + { + isQueryable = hypermedia.Content.IsQueryable(out rawData); + + if (!isQueryable && !hypermedia.Content.IsEnumerable(out rawData)) + { + throw new InvalidOperationException("HypermediaResponse.Content must be IQueryable or IEnumerable to use ODataLite. Pass a queryable or enumerable, or remove this attribute."); + } + } + else + { + throw new InvalidOperationException("Data must be IQueryable or IEnumerable to use ODataLite. Pass a queryable or enumerable, or remove this attribute."); + } } var itemType = rawData.GetType().GetGenericArguments().First(); @@ -55,7 +68,7 @@ public override async Task OnResultExecutionAsync(ResultExecutingContext context applyMethod = applyMethod.MakeGenericMethod(itemType); - if (applyMethod.Invoke(null, new object[] { context, rawData, ok.Value as HypermediaResponse }) is Task result) + if (applyMethod.Invoke(null, new object[] { context, rawData, ok.Value as HypermediaResponse, isQueryable }) is Task result) { context.Result = await result; } @@ -63,7 +76,7 @@ public override async Task OnResultExecutionAsync(ResultExecutingContext context await base.OnResultExecutionAsync(context, next); } - private static async Task ApplyODataAsync(ActionContext context, IQueryable rawData, HypermediaResponse hypermediaResponse) + private static async Task ApplyODataAsync(ActionContext context, IQueryable rawData, HypermediaResponse hypermediaResponse, bool isQueryable) { var data = (IQueryable)rawData; @@ -74,7 +87,7 @@ private static async Task ApplyODataAsync(ActionContext context if (query.HasParam("$filter", out var filterValue)) { - data = ODataFilterOperator.Apply(data, properties.Value, filterValue); + data = ODataFilterOperator.Apply(data, properties.Value, filterValue, isQueryable); } if (query.HasParam("$orderby", out var orderByValue))