Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/aspnetwebstack.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoryoussefm <youssefm@microsoft.com>2012-09-19 23:55:10 +0400
committeryoussefm <youssefm@microsoft.com>2012-09-22 01:43:35 +0400
commit03c8721af64344d9fa1960c4037706e6e04b8fb3 (patch)
treedb992738b6bf57ecb56555f61314822c68550c9c
parent2c75266574f209c03efc0b0534cc2cf8d98b0280 (diff)
[OData] Add query support for enum properties
This changeset adds support for querying againt enums. Enums can be compared to other enums as well as their string representation. Flags use comma-separated values: e.g. http://localhost/Employees?$filter=Type eq 'Manager' http://localhost/Employees?$filter=Type eq 'HR, Manager'
-rw-r--r--src/System.Web.Http.OData/OData/Formatter/EdmLibHelpers.cs4
-rw-r--r--src/System.Web.Http.OData/OData/Query/Expressions/FilterBinder.cs159
-rw-r--r--src/System.Web.Http.OData/Properties/SRResources.Designer.cs18
-rw-r--r--src/System.Web.Http.OData/Properties/SRResources.resx6
-rw-r--r--test/System.Web.Http.OData.Test/OData/Builder/TestModels/EnumModel.cs16
-rw-r--r--test/System.Web.Http.OData.Test/OData/Query/FilterQueryOptionTest.cs144
-rw-r--r--test/System.Web.Http.OData.Test/OData/Query/OrderByQueryOptionTest.cs23
-rw-r--r--test/System.Web.Http.OData.Test/System.Web.Http.OData.Test.csproj1
8 files changed, 336 insertions, 35 deletions
diff --git a/src/System.Web.Http.OData/OData/Formatter/EdmLibHelpers.cs b/src/System.Web.Http.OData/OData/Formatter/EdmLibHelpers.cs
index 160f041e..16dc5f18 100644
--- a/src/System.Web.Http.OData/OData/Formatter/EdmLibHelpers.cs
+++ b/src/System.Web.Http.OData/OData/Formatter/EdmLibHelpers.cs
@@ -95,8 +95,8 @@ namespace System.Web.Http.OData.Formatter
throw Error.ArgumentNull("edmModel");
}
- IEdmPrimitiveType primitiveType;
- if (_builtInTypesMapping.TryGetValue(clrType, out primitiveType))
+ IEdmPrimitiveType primitiveType = GetEdmPrimitiveTypeOrNull(clrType);
+ if (primitiveType != null)
{
return primitiveType;
}
diff --git a/src/System.Web.Http.OData/OData/Query/Expressions/FilterBinder.cs b/src/System.Web.Http.OData/OData/Query/Expressions/FilterBinder.cs
index 980e6be9..54f998e8 100644
--- a/src/System.Web.Http.OData/OData/Query/Expressions/FilterBinder.cs
+++ b/src/System.Web.Http.OData/OData/Query/Expressions/FilterBinder.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Data.Linq;
using System.Diagnostics.Contracts;
+using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
@@ -273,7 +274,16 @@ namespace System.Web.Http.OData.Query.Expressions
}
else
{
- return Expression.Convert(source, conversionType);
+ Type sourceUnderlyingType = Nullable.GetUnderlyingType(source.Type) ?? source.Type;
+ if (sourceUnderlyingType.IsEnum)
+ {
+ // we handle enum conversions ourselves
+ return source;
+ }
+ else
+ {
+ return Expression.Convert(source, conversionType);
+ }
}
}
@@ -606,6 +616,7 @@ namespace System.Web.Http.OData.Query.Expressions
Contract.Assert("concat" == node.Name);
Expression[] arguments = BindArguments(node.Arguments);
+ ValidateAllStringArguments(node.Name, arguments);
Contract.Assert(arguments.Length == 2 && arguments[0].Type == typeof(string) && arguments[1].Type == typeof(string));
@@ -617,6 +628,7 @@ namespace System.Web.Http.OData.Query.Expressions
Contract.Assert("trim" == node.Name);
Expression[] arguments = BindArguments(node.Arguments);
+ ValidateAllStringArguments(node.Name, arguments);
Contract.Assert(arguments.Length == 1 && arguments[0].Type == typeof(string));
@@ -628,6 +640,7 @@ namespace System.Web.Http.OData.Query.Expressions
Contract.Assert("toupper" == node.Name);
Expression[] arguments = BindArguments(node.Arguments);
+ ValidateAllStringArguments(node.Name, arguments);
Contract.Assert(arguments.Length == 1 && arguments[0].Type == typeof(string));
@@ -639,6 +652,7 @@ namespace System.Web.Http.OData.Query.Expressions
Contract.Assert("tolower" == node.Name);
Expression[] arguments = BindArguments(node.Arguments);
+ ValidateAllStringArguments(node.Name, arguments);
Contract.Assert(arguments.Length == 1 && arguments[0].Type == typeof(string));
@@ -650,6 +664,7 @@ namespace System.Web.Http.OData.Query.Expressions
Contract.Assert("indexof" == node.Name);
Expression[] arguments = BindArguments(node.Arguments);
+ ValidateAllStringArguments(node.Name, arguments);
Contract.Assert(arguments.Length == 2 && arguments[0].Type == typeof(string) && arguments[1].Type == typeof(string));
@@ -662,12 +677,16 @@ namespace System.Web.Http.OData.Query.Expressions
Expression[] arguments = BindArguments(node.Arguments);
- Contract.Assert((arguments.Length == 2 && arguments[0].Type == typeof(string) && IsInteger(arguments[1].Type)) ||
- (arguments.Length == 3 && arguments[0].Type == typeof(string) && IsInteger(arguments[1].Type) && IsInteger(arguments[2].Type)));
-
Expression functionCall;
if (arguments.Length == 2)
{
+ if (arguments[0].Type != typeof(string) || !IsInteger(arguments[1].Type))
+ {
+ throw new ODataException(Error.Format(SRResources.FunctionNotSupportedOnEnum, node.Name));
+ }
+
+ Contract.Assert(arguments[0].Type == typeof(string));
+
// When null propagation is allowed, we use a safe version of String.Substring(int).
// But for providers that would not recognize custom expressions like this, we map
// directly to String.Substring(int)
@@ -683,7 +702,13 @@ namespace System.Web.Http.OData.Query.Expressions
}
else
{
+ if (arguments[0].Type != typeof(string))
+ {
+ throw new ODataException(Error.Format(SRResources.FunctionNotSupportedOnEnum, node.Name));
+ }
+
// arguments.Length == 3 implies String.Substring(int, int)
+ Contract.Assert(arguments.Length == 3 && arguments[0].Type == typeof(string) && IsInteger(arguments[1].Type) && IsInteger(arguments[2].Type));
// When null propagation is allowed, we use a safe version of String.Substring(int, int).
// But for providers that would not recognize custom expressions like this, we map
@@ -707,6 +732,7 @@ namespace System.Web.Http.OData.Query.Expressions
Contract.Assert("length" == node.Name);
Expression[] arguments = BindArguments(node.Arguments);
+ ValidateAllStringArguments(node.Name, arguments);
Contract.Assert(arguments.Length == 1 && arguments[0].Type == typeof(string));
@@ -718,6 +744,7 @@ namespace System.Web.Http.OData.Query.Expressions
Contract.Assert("substringof" == node.Name);
Expression[] arguments = BindArguments(node.Arguments);
+ ValidateAllStringArguments(node.Name, arguments);
Contract.Assert(arguments.Length == 2 && arguments[0].Type == typeof(string) && arguments[1].Type == typeof(string));
@@ -730,6 +757,7 @@ namespace System.Web.Http.OData.Query.Expressions
Contract.Assert("startswith" == node.Name);
Expression[] arguments = BindArguments(node.Arguments);
+ ValidateAllStringArguments(node.Name, arguments);
Contract.Assert(arguments.Length == 2 && arguments[0].Type == typeof(string) && arguments[1].Type == typeof(string));
@@ -741,6 +769,7 @@ namespace System.Web.Http.OData.Query.Expressions
Contract.Assert("endswith" == node.Name);
Expression[] arguments = BindArguments(node.Arguments);
+ ValidateAllStringArguments(node.Name, arguments);
Contract.Assert(arguments.Length == 2 && arguments[0].Type == typeof(string) && arguments[1].Type == typeof(string));
@@ -752,6 +781,14 @@ namespace System.Web.Http.OData.Query.Expressions
return nodes.OfType<SingleValueQueryNode>().Select(n => Bind(n)).ToArray();
}
+ private static void ValidateAllStringArguments(string functionName, Expression[] arguments)
+ {
+ if (arguments.Any(arg => arg.Type != typeof(string)))
+ {
+ throw new ODataException(Error.Format(SRResources.FunctionNotSupportedOnEnum, functionName));
+ }
+ }
+
private Expression BindAllQueryNode(AllQueryNode allQueryNode)
{
ParameterExpression allIt = HandleLambdaParameters(allQueryNode.Parameters);
@@ -881,36 +918,44 @@ namespace System.Web.Http.OData.Query.Expressions
Expression convertedExpression = null;
- switch (Type.GetTypeCode(sourceType))
+ if (sourceType.IsEnum)
{
- case TypeCode.UInt16:
- case TypeCode.UInt32:
- case TypeCode.UInt64:
- convertedExpression = Expression.Convert(ExtractValueFromNullableExpression(source), conversionType);
- break;
-
- case TypeCode.Char:
- convertedExpression = Expression.Call(ExtractValueFromNullableExpression(source), "ToString", typeArguments: null, arguments: null);
- break;
-
- case TypeCode.Object:
- if (sourceType == typeof(char[]))
- {
- convertedExpression = Expression.New(typeof(string).GetConstructor(new[] { typeof(char[]) }), source);
- }
- else if (sourceType == typeof(XElement))
- {
- convertedExpression = Expression.Call(source, "ToString", typeArguments: null, arguments: null);
- }
- else if (sourceType == typeof(Binary))
- {
- convertedExpression = Expression.Call(source, "ToArray", typeArguments: null, arguments: null);
- }
- break;
-
- default:
- Contract.Assert(false, Error.Format("missing non-standard type support for {0}", sourceType.Name));
- break;
+ // we handle enum conversions ourselves
+ convertedExpression = source;
+ }
+ else
+ {
+ switch (Type.GetTypeCode(sourceType))
+ {
+ case TypeCode.UInt16:
+ case TypeCode.UInt32:
+ case TypeCode.UInt64:
+ convertedExpression = Expression.Convert(ExtractValueFromNullableExpression(source), conversionType);
+ break;
+
+ case TypeCode.Char:
+ convertedExpression = Expression.Call(ExtractValueFromNullableExpression(source), "ToString", typeArguments: null, arguments: null);
+ break;
+
+ case TypeCode.Object:
+ if (sourceType == typeof(char[]))
+ {
+ convertedExpression = Expression.New(typeof(string).GetConstructor(new[] { typeof(char[]) }), source);
+ }
+ else if (sourceType == typeof(XElement))
+ {
+ convertedExpression = Expression.Call(source, "ToString", typeArguments: null, arguments: null);
+ }
+ else if (sourceType == typeof(Binary))
+ {
+ convertedExpression = Expression.Call(source, "ToArray", typeArguments: null, arguments: null);
+ }
+ break;
+
+ default:
+ Contract.Assert(false, Error.Format("missing non-standard type support for {0}", sourceType.Name));
+ break;
+ }
}
if (_querySettings.HandleNullPropagation == HandleNullPropagationOption.True && IsNullable(source.Type))
@@ -934,6 +979,24 @@ namespace System.Web.Http.OData.Query.Expressions
{
ExpressionType binaryExpressionType;
+ // When comparing an enum to a string, parse the string, convert both to the enum underlying type, and compare the values
+ // When comparing an enum to an enum, convert both to the underlying type, and compare the values
+ Type leftUnderlyingType = Nullable.GetUnderlyingType(left.Type) ?? left.Type;
+ Type rightUnderlyingType = Nullable.GetUnderlyingType(right.Type) ?? right.Type;
+
+ if (leftUnderlyingType.IsEnum)
+ {
+ Type enumUnderlyingType = Enum.GetUnderlyingType(leftUnderlyingType);
+ left = Expression.Convert(left, left.Type.IsEnum ? enumUnderlyingType : ToNullable(enumUnderlyingType));
+ right = ConvertToEnumUnderlyingType(right, leftUnderlyingType, enumUnderlyingType);
+ }
+ else if (rightUnderlyingType.IsEnum)
+ {
+ Type enumUnderlyingType = Enum.GetUnderlyingType(rightUnderlyingType);
+ right = Expression.Convert(right, right.Type.IsEnum ? enumUnderlyingType : ToNullable(enumUnderlyingType));
+ left = ConvertToEnumUnderlyingType(left, rightUnderlyingType, enumUnderlyingType);
+ }
+
if (left.Type != right.Type)
{
// one of them must be nullable and the other is not.
@@ -951,6 +1014,36 @@ namespace System.Web.Http.OData.Query.Expressions
}
}
+ private static Expression ConvertToEnumUnderlyingType(Expression expression, Type enumType, Type enumUnderlyingType)
+ {
+ if (expression.NodeType == ExpressionType.Constant)
+ {
+ ConstantExpression constantExpression = expression as ConstantExpression;
+ Contract.Assert(constantExpression != null);
+ if (constantExpression.Value == null)
+ {
+ return expression;
+ }
+ else
+ {
+ Contract.Assert(constantExpression.Type == typeof(string));
+ return Expression.Constant(Convert.ChangeType(Enum.Parse(enumType, constantExpression.Value as string), enumUnderlyingType, CultureInfo.InvariantCulture));
+ }
+ }
+ else if (expression.Type == enumType)
+ {
+ return Expression.Convert(expression, enumUnderlyingType);
+ }
+ else if (Nullable.GetUnderlyingType(expression.Type) == enumType)
+ {
+ return Expression.Convert(expression, typeof(Nullable<>).MakeGenericType(enumUnderlyingType));
+ }
+ else
+ {
+ throw Error.NotSupported(SRResources.ConvertToEnumFailed, enumType, expression.Type);
+ }
+ }
+
private static IEnumerable<Expression> ExtractValueFromNullableArguments(IEnumerable<Expression> arguments)
{
return arguments.Select(arg => ExtractValueFromNullableExpression(arg));
diff --git a/src/System.Web.Http.OData/Properties/SRResources.Designer.cs b/src/System.Web.Http.OData/Properties/SRResources.Designer.cs
index 812bf288..42577390 100644
--- a/src/System.Web.Http.OData/Properties/SRResources.Designer.cs
+++ b/src/System.Web.Http.OData/Properties/SRResources.Designer.cs
@@ -205,6 +205,15 @@ namespace System.Web.Http.OData.Properties {
}
/// <summary>
+ /// Looks up a localized string similar to Cannot compare an enum of type &apos;{0}&apos; to an expression of type &apos;{1}&apos;..
+ /// </summary>
+ internal static string ConvertToEnumFailed {
+ get {
+ return ResourceManager.GetString("ConvertToEnumFailed", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to {0} does not support CreateProperty..
/// </summary>
internal static string CreatePropertyNotSupported {
@@ -394,6 +403,15 @@ namespace System.Web.Http.OData.Properties {
}
/// <summary>
+ /// Looks up a localized string similar to The &apos;{0}&apos; function cannot be applied to an enumeration-typed argument..
+ /// </summary>
+ internal static string FunctionNotSupportedOnEnum {
+ get {
+ return ResourceManager.GetString("FunctionNotSupportedOnEnum", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to EditLink generation failed. Check that you have the &apos;{0}&apos; route correctly registered..
/// </summary>
internal static string GetByIdRouteMissingOrIncorrect {
diff --git a/src/System.Web.Http.OData/Properties/SRResources.resx b/src/System.Web.Http.OData/Properties/SRResources.resx
index 9b6441ea..40940609 100644
--- a/src/System.Web.Http.OData/Properties/SRResources.resx
+++ b/src/System.Web.Http.OData/Properties/SRResources.resx
@@ -396,4 +396,10 @@
<data name="PropertyAlreadyDefinedInDerivedType" xml:space="preserve">
<value>Cannot define property '{0}' in the base entity type '{1}' as the derived type '{2}' already defines it.</value>
</data>
+ <data name="ConvertToEnumFailed" xml:space="preserve">
+ <value>Cannot compare an enum of type '{0}' to an expression of type '{1}'.</value>
+ </data>
+ <data name="FunctionNotSupportedOnEnum" xml:space="preserve">
+ <value>The '{0}' function cannot be applied to an enumeration-typed argument.</value>
+ </data>
</root> \ No newline at end of file
diff --git a/test/System.Web.Http.OData.Test/OData/Builder/TestModels/EnumModel.cs b/test/System.Web.Http.OData.Test/OData/Builder/TestModels/EnumModel.cs
new file mode 100644
index 00000000..255c4cbc
--- /dev/null
+++ b/test/System.Web.Http.OData.Test/OData/Builder/TestModels/EnumModel.cs
@@ -0,0 +1,16 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+using Microsoft.TestCommon.Types;
+
+namespace System.Web.Http.OData.Builder.TestModels
+{
+ public class EnumModel
+ {
+ public int Id { get; set; }
+ public SimpleEnum Simple { get; set; }
+ public SimpleEnum? SimpleNullable { get; set; }
+ public LongEnum Long { get; set; }
+ public FlagsEnum Flag { get; set; }
+ public FlagsEnum? FlagNullable { get; set; }
+ }
+}
diff --git a/test/System.Web.Http.OData.Test/OData/Query/FilterQueryOptionTest.cs b/test/System.Web.Http.OData.Test/OData/Query/FilterQueryOptionTest.cs
index f99e3a85..c7fe97f5 100644
--- a/test/System.Web.Http.OData.Test/OData/Query/FilterQueryOptionTest.cs
+++ b/test/System.Web.Http.OData.Test/OData/Query/FilterQueryOptionTest.cs
@@ -6,9 +6,11 @@ using System.Web.Http.Dispatcher;
using System.Web.Http.OData.Builder;
using System.Web.Http.OData.Builder.TestModels;
using Microsoft.Data.Edm;
+using Microsoft.Data.OData;
using Microsoft.Data.OData.Query;
using Microsoft.Data.OData.Query.SemanticAst;
using Microsoft.TestCommon;
+using Microsoft.TestCommon.Types;
namespace System.Web.Http.OData.Query
{
@@ -91,6 +93,91 @@ namespace System.Web.Http.OData.Query
}
}
+ // Legal filter queries usable against EnumModelTestData.
+ // Tuple is: filter, expected list of customer ID's
+ public static TheoryDataSet<string, int[]> EnumModelTestFilters
+ {
+ get
+ {
+ return new TheoryDataSet<string, int[]>
+ {
+ // Simple Enums
+ { "Simple eq 'First'", new int[] { 1, 3 } },
+ { "'First' eq Simple", new int[] { 1, 3 } },
+ { "Simple eq null", new int[] { } },
+ { "null eq Simple", new int[] { } },
+ { "Simple eq SimpleNullable", new int[] { 1 } },
+ { "SimpleNullable eq 'First'", new int[] { 1 } },
+ { "'First' eq SimpleNullable", new int[] { 1 } },
+ { "SimpleNullable eq null", new int[] { 3 } },
+ { "null eq SimpleNullable", new int[] { 3 } },
+
+ // Long enums
+ { "Long eq 'SecondLong'", new int[] { 2 } },
+
+ // Flag enums
+ { "Flag eq 'One, Four'", new int[] { 1 } },
+ { "'One, Four' eq Flag", new int[] { 1 } },
+ { "Flag eq null", new int[] { } },
+ { "null eq Flag", new int[] { } },
+ { "Flag eq FlagNullable", new int[] { 1 } },
+ { "FlagNullable eq 'One, Four'", new int[] { 1 } },
+ { "'One, Four' eq FlagNullable", new int[] { 1 } },
+ { "FlagNullable eq null", new int[] { 3 } },
+ { "null eq FlagNullable", new int[] { 3 } },
+
+ // Flag enums with different formats
+ { "Flag eq 'One,Four'", new int[] { 1 } },
+ { "Flag eq 'One, Four'", new int[] { 1 } },
+ { "Flag eq 'Four, One'", new int[] { 1 } },
+
+ // Other expressions
+ { "Flag ne 'One, Four'", new int[] { 2, 3 } },
+ { "Flag eq FlagNullable and Simple eq SimpleNullable", new int[] { 1 } },
+ { "Simple gt 'First'", new int[] { 2 } },
+ { "Flag ge 'Four,One'", new int[] { 1, 3 } }
+ };
+ }
+ }
+
+ // Test data used by EnumModelTestFilters TheoryDataSet
+ public static List<EnumModel> EnumModelTestData
+ {
+ get
+ {
+ return new List<EnumModel>()
+ {
+ new EnumModel()
+ {
+ Id = 1,
+ Simple = SimpleEnum.First,
+ SimpleNullable = SimpleEnum.First,
+ Long = LongEnum.ThirdLong,
+ Flag = FlagsEnum.One | FlagsEnum.Four,
+ FlagNullable = FlagsEnum.One | FlagsEnum.Four
+ },
+ new EnumModel()
+ {
+ Id = 2,
+ Simple = SimpleEnum.Third,
+ SimpleNullable = SimpleEnum.Second,
+ Long = LongEnum.SecondLong,
+ Flag = FlagsEnum.One | FlagsEnum.Two,
+ FlagNullable = FlagsEnum.Two | FlagsEnum.Four
+ },
+ new EnumModel()
+ {
+ Id = 3,
+ Simple = SimpleEnum.First,
+ SimpleNullable = null,
+ Long = LongEnum.FirstLong,
+ Flag = FlagsEnum.Two | FlagsEnum.Four,
+ FlagNullable = null
+ }
+ };
+ }
+ }
+
[Fact]
public void ConstructorNullContextThrows()
{
@@ -241,5 +328,62 @@ namespace System.Web.Http.OData.Query
customerIds,
actualCustomers.Select(customer => customer.CustomerId));
}
+
+ [Theory]
+ [PropertyData("EnumModelTestFilters")]
+ public void ApplyToEnums_ReturnsCorrectQueryable(string filter, int[] enumModelIds)
+ {
+ // Arrange
+ var builder = new ODataConventionModelBuilder();
+ builder.EntitySet<EnumModel>("EnumModels");
+ var model = builder.GetEdmModel();
+
+ var context = new ODataQueryContext(model, typeof(EnumModel), "EnumModels");
+ var filterOption = new FilterQueryOption(filter, context);
+ IEnumerable<EnumModel> enumModels = EnumModelTestData;
+
+ // Act
+ IQueryable queryable = filterOption.ApplyTo(enumModels.AsQueryable(), new ODataQuerySettings { HandleNullPropagation = HandleNullPropagationOption.True });
+
+ // Assert
+ Assert.NotNull(queryable);
+ IEnumerable<EnumModel> actualCustomers = Assert.IsAssignableFrom<IEnumerable<EnumModel>>(queryable);
+ Assert.Equal(
+ enumModelIds,
+ actualCustomers.Select(enumModel => enumModel.Id));
+ }
+
+ [Theory]
+ [InlineData("length(Simple) eq 5", "The 'length' function cannot be applied to an enumeration-typed argument.")]
+ [InlineData("length(SimpleNullable) eq 5", "The 'length' function cannot be applied to an enumeration-typed argument.")]
+ [InlineData("length(Flag) eq 5", "The 'length' function cannot be applied to an enumeration-typed argument.")]
+ [InlineData("length(FlagNullable) eq 5", "The 'length' function cannot be applied to an enumeration-typed argument.")]
+ [InlineData("substringof('foo', Simple) eq true", "The 'substringof' function cannot be applied to an enumeration-typed argument.")]
+ [InlineData("startswith(Simple, 'foo') eq true", "The 'startswith' function cannot be applied to an enumeration-typed argument.")]
+ [InlineData("endswith(Simple, 'foo') eq true", "The 'endswith' function cannot be applied to an enumeration-typed argument.")]
+ [InlineData("tolower(Simple) eq 'foo'", "The 'tolower' function cannot be applied to an enumeration-typed argument.")]
+ [InlineData("toupper(Simple) eq 'foo'", "The 'toupper' function cannot be applied to an enumeration-typed argument.")]
+ [InlineData("trim(Simple) eq 'foo'", "The 'trim' function cannot be applied to an enumeration-typed argument.")]
+ [InlineData("indexof(Simple, 'foo') eq 2", "The 'indexof' function cannot be applied to an enumeration-typed argument.")]
+ [InlineData("substring(Simple, 3) eq 'foo'", "The 'substring' function cannot be applied to an enumeration-typed argument.")]
+ [InlineData("substring(Simple, 1, 3) eq 'foo'", "The 'substring' function cannot be applied to an enumeration-typed argument.")]
+ [InlineData("concat(Simple, 'bar') eq 'foo'", "The 'concat' function cannot be applied to an enumeration-typed argument.")]
+ public void ApplyToEnums_ThrowsNotSupported_ForStringFunctions(string filter, string exceptionMessage)
+ {
+ // Arrange
+ var builder = new ODataConventionModelBuilder();
+ builder.EntitySet<EnumModel>("EnumModels");
+ var model = builder.GetEdmModel();
+
+ var context = new ODataQueryContext(model, typeof(EnumModel), "EnumModels");
+ var filterOption = new FilterQueryOption(filter, context);
+ IEnumerable<EnumModel> enumModels = EnumModelTestData;
+
+ // Act
+ Assert.Throws<ODataException>(
+ () => filterOption.ApplyTo(enumModels.AsQueryable(), new ODataQuerySettings { HandleNullPropagation = HandleNullPropagationOption.True }),
+ exceptionMessage
+ );
+ }
}
}
diff --git a/test/System.Web.Http.OData.Test/OData/Query/OrderByQueryOptionTest.cs b/test/System.Web.Http.OData.Test/OData/Query/OrderByQueryOptionTest.cs
index 5f26a6cb..d4403ca8 100644
--- a/test/System.Web.Http.OData.Test/OData/Query/OrderByQueryOptionTest.cs
+++ b/test/System.Web.Http.OData.Test/OData/Query/OrderByQueryOptionTest.cs
@@ -198,5 +198,28 @@ namespace System.Web.Http.OData.Query
Assert.Equal(3, results[1].CustomerId);
Assert.Equal(2, results[2].CustomerId);
}
+
+ [Fact]
+ public void ApplyToEnums_ReturnsCorrectQueryable()
+ {
+ // Arrange
+ var builder = new ODataConventionModelBuilder();
+ builder.EntitySet<EnumModel>("EnumModels");
+ var model = builder.GetEdmModel();
+
+ var context = new ODataQueryContext(model, typeof(EnumModel), "EnumModels");
+ var orderbyOption = new OrderByQueryOption("Flag", context);
+ IEnumerable<EnumModel> enumModels = FilterQueryOptionTest.EnumModelTestData;
+
+ // Act
+ IQueryable queryable = orderbyOption.ApplyTo(enumModels.AsQueryable());
+
+ // Assert
+ Assert.NotNull(queryable);
+ IEnumerable<EnumModel> actualCustomers = Assert.IsAssignableFrom<IEnumerable<EnumModel>>(queryable);
+ Assert.Equal(
+ new int[] { 2, 1, 3 },
+ actualCustomers.Select(enumModel => enumModel.Id));
+ }
}
}
diff --git a/test/System.Web.Http.OData.Test/System.Web.Http.OData.Test.csproj b/test/System.Web.Http.OData.Test/System.Web.Http.OData.Test.csproj
index a5790564..3b4aadbb 100644
--- a/test/System.Web.Http.OData.Test/System.Web.Http.OData.Test.csproj
+++ b/test/System.Web.Http.OData.Test/System.Web.Http.OData.Test.csproj
@@ -106,6 +106,7 @@
<Compile Include="OData\Builder\ActionConfigurationTest.cs" />
<Compile Include="OData\Builder\ParameterConfigurationTest.cs" />
<Compile Include="OData\Builder\CollectionPropertyConfigurationTest.cs" />
+ <Compile Include="OData\Builder\TestModels\EnumModel.cs" />
<Compile Include="OData\Formatter\PartialTrustTest.cs" />
<Compile Include="OData\Builder\EdmTypeConfigurationExtensionsTest.cs" />
<Compile Include="OData\Builder\TestModels\InheritanceModels.cs" />