diff options
author | youssefm <youssefm@microsoft.com> | 2012-09-19 23:55:10 +0400 |
---|---|---|
committer | youssefm <youssefm@microsoft.com> | 2012-09-22 01:43:35 +0400 |
commit | 03c8721af64344d9fa1960c4037706e6e04b8fb3 (patch) | |
tree | db992738b6bf57ecb56555f61314822c68550c9c | |
parent | 2c75266574f209c03efc0b0534cc2cf8d98b0280 (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'
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 '{0}' to an expression of type '{1}'.. + /// </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 '{0}' 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 '{0}' 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" /> |