Enum support for Action and Function

Enum with type attribute with different metadata level mode
Enable Enum in FilterQueryValidator
Enum support for raw value
This commit is contained in:
fenzhao 2014-01-14 18:47:54 +08:00
Родитель a08bb56452
Коммит c850136c24
60 изменённых файлов: 1504 добавлений и 340 удалений

Просмотреть файл

@ -142,12 +142,6 @@ namespace System.Web.Http.OData.Builder
throw Error.InvalidOperation(SRResources.ReturnEntityWithoutEntitySet, configuration.FullName);
}
if (configuration == null)
{
ModelBuilder.AddComplexType(returnType);
configuration = ModelBuilder.GetTypeConfigurationOrNull(typeof(TReturnType));
}
ReturnType = configuration;
ReturnsImplementation<TReturnType>();
return this;
}
@ -172,12 +166,6 @@ namespace System.Web.Http.OData.Builder
throw Error.InvalidOperation(SRResources.ReturnEntityCollectionWithoutEntitySet, edmElementType.FullName);
}
if (edmElementType == null)
{
ModelBuilder.AddComplexType(clrElementType);
edmElementType = ModelBuilder.GetTypeConfigurationOrNull(clrElementType);
}
ReturnType = new CollectionTypeConfiguration(edmElementType, clrCollectionType);
ReturnsCollectionImplementation<TReturnElementType>();
return this;
}

Просмотреть файл

@ -133,16 +133,15 @@ namespace System.Web.Http.OData.Builder.Conventions
{
Contract.Assert(value != null);
// TODO: Delete enum processing when OData beta1 or later is referenced.
Type underlyingType = Nullable.GetUnderlyingType(value.GetType()) ?? value.GetType();
Contract.Assert(EdmLibHelpers.GetEdmPrimitiveTypeOrNull(value.GetType()) != null || underlyingType.IsEnum);
if (underlyingType.IsEnum)
// TODO 1608: Delete enum processing when OData beta1 or later is referenced.
Type type = value.GetType();
if (type.IsEnum)
{
return String.Format(CultureInfo.InvariantCulture,
"{0}'{1}'", underlyingType.Name, value);
"{0}'{1}'", type.EdmFullName(), value);
}
Contract.Assert(EdmLibHelpers.GetEdmPrimitiveTypeOrNull(type) != null);
value = ODataPrimitiveSerializer.ConvertUnsupportedPrimitives(value);
return ODataUriUtils.ConvertToUriLiteral(value, ODataVersion.V4);
}

Просмотреть файл

@ -192,7 +192,10 @@ namespace System.Web.Http.OData.Builder
Dictionary<string, EdmEntitySet> edmEntitySetMap,
bool isAction)
{
IEdmTypeReference returnReference = GetEdmTypeReference(edmTypeMap, procedure.ReturnType, nullable: true);
IEdmTypeReference returnReference = GetEdmTypeReference(
edmTypeMap,
procedure.ReturnType,
procedure.ReturnType != null && EdmLibHelpers.IsNullable(procedure.ReturnType.ClrType));
IEdmExpression expression = GetEdmEntitySetExpression(edmEntitySetMap, procedure);
IEdmPathExpression pathExpression = procedure.EntitySetPath != null
? new EdmPathExpression(procedure.EntitySetPath)
@ -357,37 +360,51 @@ namespace System.Web.Http.OData.Builder
if (kind == EdmTypeKind.Collection)
{
CollectionTypeConfiguration collectionType = configuration as CollectionTypeConfiguration;
EdmCollectionType edmCollectionType = new EdmCollectionType(GetEdmTypeReference(availableTypes, collectionType.ElementType, false));
EdmCollectionType edmCollectionType = new EdmCollectionType(GetEdmTypeReference(
availableTypes,
collectionType.ElementType,
EdmLibHelpers.IsNullable(collectionType.ElementType.ClrType)));
return new EdmCollectionTypeReference(edmCollectionType, nullable);
}
else if (availableTypes.ContainsKey(configuration.ClrType))
{
IEdmType type = availableTypes[configuration.ClrType];
if (kind == EdmTypeKind.Complex)
{
return new EdmComplexTypeReference((IEdmComplexType)type, nullable);
}
else if (kind == EdmTypeKind.Entity)
{
return new EdmEntityTypeReference((IEdmEntityType)type, nullable);
}
else if (kind == EdmTypeKind.Enum)
{
return new EdmEnumTypeReference((IEdmEnumType)type, nullable);
}
else
{
throw Error.InvalidOperation(SRResources.UnsupportedEdmTypeKind, kind.ToString());
}
}
else if (configuration.Kind == EdmTypeKind.Primitive)
{
PrimitiveTypeConfiguration primitiveTypeConfiguration = configuration as PrimitiveTypeConfiguration;
return new EdmPrimitiveTypeReference(primitiveTypeConfiguration.EdmPrimitiveType, nullable);
}
else
{
throw Error.InvalidOperation(SRResources.NoMatchingIEdmTypeFound, configuration.FullName);
Type configurationClrType = TypeHelper.GetUnderlyingTypeOrSelf(configuration.ClrType);
if (!configurationClrType.IsEnum)
{
configurationClrType = configuration.ClrType;
}
IEdmType type;
if (availableTypes.TryGetValue(configurationClrType, out type))
{
if (kind == EdmTypeKind.Complex)
{
return new EdmComplexTypeReference((IEdmComplexType)type, nullable);
}
else if (kind == EdmTypeKind.Entity)
{
return new EdmEntityTypeReference((IEdmEntityType)type, nullable);
}
else if (kind == EdmTypeKind.Enum)
{
return new EdmEnumTypeReference((IEdmEnumType)type, nullable);
}
else
{
throw Error.InvalidOperation(SRResources.UnsupportedEdmTypeKind, kind.ToString());
}
}
else if (configuration.Kind == EdmTypeKind.Primitive)
{
PrimitiveTypeConfiguration primitiveTypeConfiguration = configuration as PrimitiveTypeConfiguration;
return new EdmPrimitiveTypeReference(primitiveTypeConfiguration.EdmPrimitiveType, nullable);
}
else
{
throw Error.InvalidOperation(SRResources.NoMatchingIEdmTypeFound, configuration.FullName);
}
}
}

Просмотреть файл

@ -52,7 +52,7 @@ namespace System.Web.Http.OData.Builder
private void CreateEdmTypeHeader(IEdmTypeConfiguration config)
{
if (!_types.ContainsKey(config.ClrType))
if (GetEdmType(config.ClrType) == null)
{
if (config.Kind == EdmTypeKind.Complex)
{
@ -68,7 +68,7 @@ namespace System.Web.Http.OData.Builder
if (entity.BaseType != null)
{
CreateEdmTypeHeader(entity.BaseType);
baseType = _types[entity.BaseType.ClrType] as IEdmEntityType;
baseType = GetEdmType(entity.BaseType.ClrType) as IEdmEntityType;
Contract.Assert(baseType != null);
}
@ -89,7 +89,7 @@ namespace System.Web.Http.OData.Builder
private void CreateEdmTypeBody(IEdmTypeConfiguration config)
{
IEdmType edmType = _types[config.ClrType];
IEdmType edmType = GetEdmType(config.ClrType);
if (edmType.TypeKind == EdmTypeKind.Complex)
{
@ -136,7 +136,7 @@ namespace System.Web.Http.OData.Builder
case PropertyKind.Complex:
ComplexPropertyConfiguration complexProperty = property as ComplexPropertyConfiguration;
IEdmComplexType complexType = _types[complexProperty.RelatedClrType] as IEdmComplexType;
IEdmComplexType complexType = GetEdmType(complexProperty.RelatedClrType) as IEdmComplexType;
edmProperty = type.AddStructuralProperty(
complexProperty.Name,
@ -173,27 +173,38 @@ namespace System.Web.Http.OData.Builder
private IEdmProperty CreateStructuralTypeCollectionPropertyBody(EdmStructuredType type, CollectionPropertyConfiguration collectionProperty)
{
IEdmTypeReference elementTypeReference = null;
Type clrType = Nullable.GetUnderlyingType(collectionProperty.ElementType) ?? collectionProperty.ElementType;
IEdmType edmType;
Type clrType = TypeHelper.GetUnderlyingTypeOrSelf(collectionProperty.ElementType);
if (clrType.IsEnum)
{
if (!_types.TryGetValue(clrType, out edmType))
IEdmType edmType = GetEdmType(clrType);
if (edmType == null)
{
throw Error.InvalidOperation(SRResources.EnumTypeNotExisting, clrType.Name);
throw Error.InvalidOperation(SRResources.EnumTypeDoesNotExist, clrType.Name);
}
IEdmEnumType enumElementType = (IEdmEnumType)edmType;
elementTypeReference = new EdmEnumTypeReference(enumElementType, collectionProperty.ElementType.IsNullable());
}
else if (_types.TryGetValue(collectionProperty.ElementType, out edmType))
{
IEdmComplexType elementType = (IEdmComplexType)edmType;
elementTypeReference = new EdmComplexTypeReference(elementType, false);
bool isNullable = collectionProperty.ElementType != clrType;
elementTypeReference = new EdmEnumTypeReference(enumElementType, isNullable);
}
else
{
elementTypeReference = EdmLibHelpers.GetEdmPrimitiveTypeReferenceOrNull(collectionProperty.ElementType);
IEdmType edmType = GetEdmType(collectionProperty.ElementType);
if (edmType != null)
{
IEdmComplexType elementType = edmType as IEdmComplexType;
Contract.Assert(elementType != null);
elementTypeReference = new EdmComplexTypeReference(elementType, false);
}
else
{
elementTypeReference =
EdmLibHelpers.GetEdmPrimitiveTypeReferenceOrNull(collectionProperty.ElementType);
Contract.Assert(elementTypeReference != null);
}
}
return type.AddStructuralProperty(
collectionProperty.Name,
new EdmCollectionTypeReference(
@ -203,12 +214,14 @@ namespace System.Web.Http.OData.Builder
private IEdmProperty CreateStructuralTypeEnumPropertyBody(EdmStructuredType type, StructuralTypeConfiguration config, EnumPropertyConfiguration enumProperty)
{
Type enumPropertyType = Nullable.GetUnderlyingType(enumProperty.RelatedClrType) ?? enumProperty.RelatedClrType;
IEdmType edmType;
if (!_types.TryGetValue(enumPropertyType, out edmType))
Type enumPropertyType = TypeHelper.GetUnderlyingTypeOrSelf(enumProperty.RelatedClrType);
IEdmType edmType = GetEdmType(enumPropertyType);
if (edmType == null)
{
throw Error.InvalidOperation(SRResources.EnumTypeNotExisting, enumPropertyType.Name);
throw Error.InvalidOperation(SRResources.EnumTypeDoesNotExist, enumPropertyType.Name);
}
IEdmEnumType enumType = (IEdmEnumType)edmType;
IEdmTypeReference enumTypeReference = new EdmEnumTypeReference(enumType, enumProperty.OptionalProperty);
@ -225,6 +238,7 @@ namespace System.Web.Http.OData.Builder
defaultValue: null,
concurrencyMode: enumConcurrencyMode);
}
private void CreateComplexTypeBody(EdmComplexType type, ComplexTypeConfiguration config)
{
Contract.Assert(type != null);
@ -247,7 +261,7 @@ namespace System.Web.Http.OData.Builder
EdmNavigationPropertyInfo info = new EdmNavigationPropertyInfo();
info.Name = navProp.Name;
info.TargetMultiplicity = navProp.Multiplicity;
info.Target = _types[navProp.RelatedClrType] as IEdmEntityType;
info.Target = GetEdmType(navProp.RelatedClrType) as IEdmEntityType;
//TODO: If target end has a multiplity of 1 this assumes the source end is 0..1.
// I think a better default multiplicity is *
IEdmProperty edmProperty = type.AddUnidirectionalNavigation(info);
@ -288,6 +302,16 @@ namespace System.Web.Http.OData.Builder
}
}
private IEdmType GetEdmType(Type clrType)
{
Contract.Assert(clrType != null);
IEdmType edmType;
_types.TryGetValue(clrType, out edmType);
return edmType;
}
/// <summary>
/// Builds <see cref="IEdmType"/> and <see cref="IEdmProperty"/>'s from <paramref name="configurations"/>
/// </summary>

Просмотреть файл

@ -18,6 +18,7 @@ namespace System.Web.Http.OData.Builder
private const string DefaultNamespace = "Default";
private string _namespace;
private string _name;
private NullableEnumTypeConfiguration nullableEnumTypeConfiguration = null;
/// <summary>
/// Initializes a new instance of the <see cref="EnumTypeConfiguration"/> class.
@ -236,5 +237,15 @@ namespace System.Web.Http.OData.Builder
RemovedMembers.Add(member);
}
}
internal NullableEnumTypeConfiguration GetNullableEnumTypeConfiguration()
{
if (nullableEnumTypeConfiguration == null)
{
nullableEnumTypeConfiguration = new NullableEnumTypeConfiguration(this);
}
return nullableEnumTypeConfiguration;
}
}
}

Просмотреть файл

@ -0,0 +1,58 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.CodeAnalysis;
using Microsoft.OData.Edm;
namespace System.Web.Http.OData.Builder
{
internal class NullableEnumTypeConfiguration : IEdmTypeConfiguration
{
internal NullableEnumTypeConfiguration(EnumTypeConfiguration enumTypeConfiguration)
{
this.ClrType = typeof(Nullable<>).MakeGenericType(enumTypeConfiguration.ClrType);
this.FullName = enumTypeConfiguration.FullName;
this.Namespace = enumTypeConfiguration.Namespace;
this.Name = enumTypeConfiguration.Name;
this.Kind = enumTypeConfiguration.Kind;
this.ModelBuilder = enumTypeConfiguration.ModelBuilder;
this.EnumTypeConfiguration = enumTypeConfiguration;
}
/// <summary>
/// The CLR type associated with the nullable enum type.
/// </summary>
public Type ClrType { get; private set; }
/// <summary>
/// The fullname (including namespace) of the EdmType.
/// </summary>
public string FullName { get; private set; }
/// <summary>
/// The namespace of the EdmType.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Namespace", Justification = "Namespace matches the EF naming scheme")]
public string Namespace { get; private set; }
/// <summary>
/// The name of the EdmType.
/// </summary>
public string Name { get; private set; }
/// <summary>
/// The kind of the EdmType.
/// Examples include EntityType, ComplexType, PrimitiveType, CollectionType, EnumType.
/// </summary>
public EdmTypeKind Kind { get; private set; }
/// <summary>
/// The ODataModelBuilder used to create this IEdmType.
/// </summary>
public ODataModelBuilder ModelBuilder { get; private set; }
/// <summary>
/// The EnumTypeConfiguration used to create this class.
/// </summary>
internal EnumTypeConfiguration EnumTypeConfiguration { get; private set; }
}
}

Просмотреть файл

@ -535,13 +535,17 @@ namespace System.Web.Http.OData.Builder
throw Error.ArgumentNull("type");
}
Type enumType = Nullable.GetUnderlyingType(type) ?? type;
if (enumType.IsEnum && EnumTypes.All(e => e.ClrType != enumType))
if (TypeHelper.IsEnum(type))
{
EnumTypeConfiguration enumTypeConfiguration = AddEnumType(enumType);
foreach (object member in Enum.GetValues(enumType))
Type enumType = TypeHelper.GetUnderlyingTypeOrSelf(type);
if (!EnumTypes.Any(e => e.ClrType == enumType))
{
enumTypeConfiguration.AddMember((Enum)member);
EnumTypeConfiguration enumTypeConfiguration = AddEnumType(enumType);
foreach (object member in Enum.GetValues(enumType))
{
enumTypeConfiguration.AddMember((Enum)member);
}
}
}
}
@ -552,77 +556,23 @@ namespace System.Web.Http.OData.Builder
{
Contract.Assert(property != null);
if (EdmLibHelpers.GetEdmPrimitiveTypeOrNull(property.PropertyType) != null)
PropertyKind propertyKind;
if (TryGetPropertyTypeKind(property.PropertyType, out mappedType, out propertyKind))
{
isCollection = false;
mappedType = null;
return PropertyKind.Primitive;
}
mappedType = GetStructuralTypeOrNull(property.PropertyType);
if (mappedType != null)
{
isCollection = false;
if (mappedType is ComplexTypeConfiguration)
{
return PropertyKind.Complex;
}
else if (mappedType is EnumTypeConfiguration)
{
return PropertyKind.Enum;
}
else
{
return PropertyKind.Navigation;
}
}
Type underlyingType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
if (underlyingType.IsEnum)
{
isCollection = false;
mappedType = null;
return PropertyKind.Enum;
return propertyKind;
}
Type elementType;
if (property.PropertyType.IsCollection(out elementType))
{
isCollection = true;
// Collection properties - can be a collection of primitives, enum, complex or entities.
if (EdmLibHelpers.GetEdmPrimitiveTypeOrNull(elementType) != null)
if (TryGetPropertyTypeKind(elementType, out mappedType, out propertyKind))
{
return PropertyKind.Primitive;
return propertyKind;
}
mappedType = GetStructuralTypeOrNull(elementType);
if (mappedType != null)
{
if (mappedType is ComplexTypeConfiguration)
{
return PropertyKind.Complex;
}
else if (mappedType is EntityTypeConfiguration)
{
return PropertyKind.Navigation;
}
else
{
return PropertyKind.Enum;
}
}
underlyingType = Nullable.GetUnderlyingType(elementType) ?? elementType;
if (underlyingType.IsEnum)
{
isCollection = true;
mappedType = null;
return PropertyKind.Enum;
}
// if we know nothing about this type we assume it to be an entity
// if we know nothing about this type we assume it to be collection of entities
// and patch up later
return PropertyKind.Navigation;
}
@ -633,6 +583,46 @@ namespace System.Web.Http.OData.Builder
return PropertyKind.Navigation;
}
private bool TryGetPropertyTypeKind(Type propertyType, out IEdmTypeConfiguration mappedType, out PropertyKind propertyKind)
{
Contract.Assert(propertyType != null);
if (EdmLibHelpers.GetEdmPrimitiveTypeOrNull(propertyType) != null)
{
mappedType = null;
propertyKind = PropertyKind.Primitive;
return true;
}
mappedType = GetStructuralTypeOrNull(propertyType);
if (mappedType != null)
{
if (mappedType is ComplexTypeConfiguration)
{
propertyKind = PropertyKind.Complex;
}
else if (mappedType is EnumTypeConfiguration)
{
propertyKind = PropertyKind.Enum;
}
else
{
propertyKind = PropertyKind.Navigation;
}
return true;
}
if (TypeHelper.IsEnum(propertyType))
{
propertyKind = PropertyKind.Enum;
return true;
}
propertyKind = PropertyKind.Navigation;
return false;
}
// the convention model builder MapTypes() method might have went through deep object graphs and added a bunch of types
// only to realise after applying the conventions that the user has ignored some of the properties. So, prune the unreachable stuff.
private void PruneUnreachableTypes()
@ -749,9 +739,10 @@ namespace System.Web.Http.OData.Builder
IEdmTypeConfiguration configuration = StructuralTypes.SingleOrDefault(edmType => edmType.ClrType == clrType);
if (configuration == null)
{
Type type = Nullable.GetUnderlyingType(clrType) ?? clrType;
Type type = TypeHelper.GetUnderlyingTypeOrSelf(clrType);
configuration = EnumTypes.SingleOrDefault(edmType => edmType.ClrType == type);
}
return configuration;
}

Просмотреть файл

@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Web.Http.OData.Formatter;
namespace System.Web.Http.OData.Builder
{
@ -217,12 +218,7 @@ namespace System.Web.Http.OData.Builder
internal void ReturnsImplementation<TReturnType>()
{
Type returnType = typeof(TReturnType);
IEdmTypeConfiguration configuration = ModelBuilder.GetTypeConfigurationOrNull(returnType);
if (configuration == null)
{
ModelBuilder.AddComplexType(returnType);
configuration = ModelBuilder.GetTypeConfigurationOrNull(typeof(TReturnType));
}
IEdmTypeConfiguration configuration = GetProcedureTypeConfiguration(returnType);
ReturnType = configuration;
}
@ -239,12 +235,7 @@ namespace System.Web.Http.OData.Builder
// You can imagine the override of this that takes a delegate using the correct CLR type for the return type.
Type clrCollectionType = typeof(IEnumerable<TReturnElementType>);
Type clrElementType = typeof(TReturnElementType);
IEdmTypeConfiguration edmElementType = ModelBuilder.GetTypeConfigurationOrNull(clrElementType);
if (edmElementType == null)
{
ModelBuilder.AddComplexType(clrElementType);
edmElementType = ModelBuilder.GetTypeConfigurationOrNull(clrElementType);
}
IEdmTypeConfiguration edmElementType = GetProcedureTypeConfiguration(clrElementType);
ReturnType = new CollectionTypeConfiguration(edmElementType, clrCollectionType);
}
@ -273,12 +264,7 @@ namespace System.Web.Http.OData.Builder
public ParameterConfiguration Parameter<TParameter>(string name)
{
Type type = typeof(TParameter);
IEdmTypeConfiguration parameterType = ModelBuilder.GetTypeConfigurationOrNull(type);
if (parameterType == null)
{
ModelBuilder.AddComplexType(type);
parameterType = ModelBuilder.GetTypeConfigurationOrNull(type);
}
IEdmTypeConfiguration parameterType = GetProcedureTypeConfiguration(type);
return AddParameter(name, parameterType);
}
@ -289,12 +275,7 @@ namespace System.Web.Http.OData.Builder
public ParameterConfiguration CollectionParameter<TElementType>(string name)
{
Type elementType = typeof(TElementType);
IEdmTypeConfiguration elementTypeConfiguration = ModelBuilder.GetTypeConfigurationOrNull(elementType);
if (elementTypeConfiguration == null)
{
ModelBuilder.AddComplexType(elementType);
elementTypeConfiguration = ModelBuilder.GetTypeConfigurationOrNull(elementType);
}
IEdmTypeConfiguration elementTypeConfiguration = GetProcedureTypeConfiguration(elementType);
CollectionTypeConfiguration parameterType = new CollectionTypeConfiguration(elementTypeConfiguration, typeof(IEnumerable<>).MakeGenericType(elementType));
return AddParameter(name, parameterType);
}
@ -303,5 +284,48 @@ namespace System.Web.Http.OData.Builder
/// Gets or sets the <see cref="ODataModelBuilder"/> used to create this configuration.
/// </summary>
protected ODataModelBuilder ModelBuilder { get; set; }
private IEdmTypeConfiguration GetProcedureTypeConfiguration(Type clrType)
{
Type type = TypeHelper.GetUnderlyingTypeOrSelf(clrType);
IEdmTypeConfiguration edmTypeConfiguration;
if (type.IsEnum)
{
edmTypeConfiguration = ModelBuilder.GetTypeConfigurationOrNull(type);
if (edmTypeConfiguration != null && EdmLibHelpers.IsNullable(clrType))
{
edmTypeConfiguration = ((EnumTypeConfiguration)edmTypeConfiguration).GetNullableEnumTypeConfiguration();
}
}
else
{
edmTypeConfiguration = ModelBuilder.GetTypeConfigurationOrNull(clrType);
}
if (edmTypeConfiguration == null)
{
if (type.IsEnum)
{
EnumTypeConfiguration enumTypeConfiguration = ModelBuilder.AddEnumType(type);
if (EdmLibHelpers.IsNullable(clrType))
{
edmTypeConfiguration = enumTypeConfiguration.GetNullableEnumTypeConfiguration();
}
else
{
edmTypeConfiguration = enumTypeConfiguration;
}
}
else
{
edmTypeConfiguration = ModelBuilder.AddComplexType(clrType);
}
}
return edmTypeConfiguration;
}
}
}

Просмотреть файл

@ -218,9 +218,7 @@ namespace System.Web.Http.OData.Builder
throw Error.Argument("propertyInfo", SRResources.PropertyDoesNotBelongToType, propertyInfo.Name, ClrType.FullName);
}
Type enumType = Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType;
if (!enumType.IsEnum)
if (!TypeHelper.IsEnum(propertyInfo.PropertyType))
{
throw Error.Argument("propertyInfo", SRResources.MustBeEnumProperty, propertyInfo.Name, ClrType.FullName);
}
@ -350,8 +348,7 @@ namespace System.Web.Http.OData.Builder
EdmLibHelpers.GetEdmPrimitiveTypeReferenceOrNull(propertyConfiguration.ElementType);
if (edmType == null)
{
Type type = Nullable.GetUnderlyingType(propertyConfiguration.ElementType) ?? propertyConfiguration.ElementType;
if (!type.IsEnum)
if (!TypeHelper.IsEnum(propertyConfiguration.ElementType))
{
ModelBuilder.AddComplexType(propertyConfiguration.ElementType);
}

Просмотреть файл

@ -19,7 +19,7 @@ namespace System.Web.Http.OData.Formatter.Deserialization
throw Error.ArgumentNull("type");
}
Type enumType = Nullable.GetUnderlyingType(type) ?? type;
Type enumType = TypeHelper.GetUnderlyingTypeOrSelf(type);
// if value is of the requested type nothing to do here.
if (value.GetType() == enumType)

Просмотреть файл

@ -26,6 +26,14 @@ namespace System.Web.Http.OData.Formatter.Deserialization
{
throw Error.ArgumentNull("messageReader");
}
if (type == null)
{
throw Error.ArgumentNull("type");
}
if (readContext == null)
{
throw Error.ArgumentNull("readContext");
}
IEdmTypeReference edmType = readContext.GetEdmType(type);
Contract.Assert(edmType != null);

Просмотреть файл

@ -134,7 +134,8 @@ namespace System.Web.Http.OData.Formatter
}
}
Type underlyingType = Nullable.GetUnderlyingType(clrType) ?? clrType;
Type underlyingType = TypeHelper.GetUnderlyingTypeOrSelf(clrType);
if (underlyingType.IsEnum)
{
clrType = underlyingType;

Просмотреть файл

@ -0,0 +1,28 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Web.Http.OData.Routing;
using Microsoft.OData.Edm;
namespace System.Web.Http.OData.Formatter
{
/// <summary>
/// Media type mapping that associates requests for the raw value of enum properties with
/// the text/plain content type.
/// </summary>
public class ODataEnumValueMediaTypeMapping : ODataRawValueMediaTypeMapping
{
/// <summary>
/// Initializes a new instance of the <see cref="ODataEnumValueMediaTypeMapping"/> class.
/// </summary>
public ODataEnumValueMediaTypeMapping()
: base("text/plain")
{
}
/// <inheritdoc/>
protected override bool IsMatch(PropertyAccessPathSegment propertySegment)
{
return propertySegment != null && propertySegment.Property.Type.IsEnum();
}
}
}

Просмотреть файл

@ -69,6 +69,7 @@ namespace System.Web.Http.OData.Formatter
{
ODataMediaTypeFormatter formatter = CreateFormatterWithoutMediaTypes(serializerProvider, deserializerProvider, ODataPayloadKind.Value);
formatter.MediaTypeMappings.Add(new ODataPrimitiveValueMediaTypeMapping());
formatter.MediaTypeMappings.Add(new ODataEnumValueMediaTypeMapping());
formatter.MediaTypeMappings.Add(new ODataBinaryValueMediaTypeMapping());
return formatter;
}

Просмотреть файл

@ -2,7 +2,10 @@
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Web.Http.Controllers;
using System.Web.Http.ModelBinding;
using System.Web.Http.OData.Properties;
@ -35,11 +38,19 @@ namespace System.Web.Http.OData.Formatter
return new ODataModelBinder();
}
if (TypeHelper.IsEnum(modelType))
{
return new ODataModelBinder();
}
return null;
}
internal class ODataModelBinder : IModelBinder
{
private static MethodInfo enumTryParseMethod = typeof(Enum).GetMethods()
.Single(m => m.Name == "TryParse" && m.GetParameters().Length == 2);
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We don't want to fail in model binding.")]
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
@ -96,6 +107,25 @@ namespace System.Web.Http.OData.Formatter
return null;
}
// TODO 1608: ODataUriUtils.ConvertFromUriLiteral doesn't support enum
if (TypeHelper.IsEnum(type))
{
string[] values = valueString.Split(new[] { '\'' }, StringSplitOptions.None);
Contract.Assert(values.Length == 3 && values[2] == String.Empty);
string enumValueString = values[1];
Type enumType = TypeHelper.GetUnderlyingTypeOrSelf(type);
object[] parameters = new[] { enumValueString, Enum.ToObject(type, 0) };
bool isSuccessful = (bool)enumTryParseMethod.MakeGenericMethod(enumType).Invoke(null, parameters);
if (!isSuccessful)
{
throw Error.InvalidOperation(SRResources.ModelBinderUtil_ValueCannotBeEnum, valueString, type.Name);
}
return parameters[1];
}
object value = ODataUriUtils.ConvertFromUriLiteral(valueString, ODataVersion.V4);
bool isNonStandardEdmPrimitive;

Просмотреть файл

@ -132,7 +132,7 @@ namespace System.Web.Http.OData.Formatter.Serialization
if (edmType != null)
{
if (edmType.IsPrimitive() && ODataRawValueMediaTypeMapping.IsRawValueRequest(request))
if ((edmType.IsPrimitive() || edmType.IsEnum()) && ODataRawValueMediaTypeMapping.IsRawValueRequest(request))
{
return _rawValueSerializer;
}

Просмотреть файл

@ -81,12 +81,76 @@ namespace System.Web.Http.OData.Formatter.Serialization
}
ODataEnumValue enumValue = new ODataEnumValue(value, enumType.FullName());
enumValue.SetAnnotation<SerializationTypeNameAnnotation>(new SerializationTypeNameAnnotation
{
TypeName = null
});
ODataMetadataLevel metadataLevel = writeContext != null
? writeContext.MetadataLevel
: ODataMetadataLevel.Default;
AddTypeNameAnnotationAsNeeded(enumValue, enumType, metadataLevel);
return enumValue;
}
internal static void AddTypeNameAnnotationAsNeeded(ODataEnumValue enumValue, IEdmEnumTypeReference enumType, ODataMetadataLevel metadataLevel)
{
// ODataLib normally has the caller decide whether or not to serialize properties by leaving properties
// null when values should not be serialized. The TypeName property is different and should always be
// provided to ODataLib to enable model validation. A separate annotation is used to decide whether or not
// to serialize the type name (a null value prevents serialization).
// Note that this annotation should not be used for Atom or JSON verbose formats, as it will interfere with
// the correct default behavior for those formats.
Contract.Assert(enumValue != null);
// Only add an annotation if we want to override ODataLib's default type name serialization behavior.
if (ShouldAddTypeNameAnnotation(metadataLevel))
{
string typeName;
// Provide the type name to serialize (or null to force it not to serialize).
if (ShouldSuppressTypeNameSerialization(metadataLevel))
{
typeName = null;
}
else
{
typeName = enumType.FullName();
}
enumValue.SetAnnotation(new SerializationTypeNameAnnotation
{
TypeName = typeName
});
}
}
private static bool ShouldAddTypeNameAnnotation(ODataMetadataLevel metadataLevel)
{
switch (metadataLevel)
{
case ODataMetadataLevel.Default:
case ODataMetadataLevel.MinimalMetadata:
return false;
case ODataMetadataLevel.FullMetadata:
case ODataMetadataLevel.NoMetadata:
default:
return true;
}
}
private static bool ShouldSuppressTypeNameSerialization(ODataMetadataLevel metadataLevel)
{
Contract.Assert(metadataLevel != ODataMetadataLevel.Default);
Contract.Assert(metadataLevel != ODataMetadataLevel.MinimalMetadata);
switch (metadataLevel)
{
case ODataMetadataLevel.NoMetadata:
return true;
case ODataMetadataLevel.FullMetadata:
default:
return false;
}
}
}
}

Просмотреть файл

@ -11,7 +11,7 @@ namespace System.Web.Http.OData.Formatter.Serialization
public class ODataRawValueSerializer : ODataSerializer
{
/// <summary>
/// Initializes a new instance of <see cref="ODataPrimitiveSerializer"/>.
/// Initializes a new instance of <see cref="ODataRawValueSerializer"/>.
/// </summary>
public ODataRawValueSerializer()
: base(ODataPayloadKind.Value)
@ -30,7 +30,14 @@ namespace System.Web.Http.OData.Formatter.Serialization
throw Error.ArgumentNull("graph");
}
messageWriter.WriteValue(ODataPrimitiveSerializer.ConvertUnsupportedPrimitives(graph));
if (graph.GetType().IsEnum)
{
messageWriter.WriteValue(graph.ToString());
}
else
{
messageWriter.WriteValue(ODataPrimitiveSerializer.ConvertUnsupportedPrimitives(graph));
}
}
}
}

Просмотреть файл

@ -58,9 +58,14 @@ namespace System.Web.Http.OData.Query
/// </summary>
Not = 0x100,
/// <summary>
/// A value that corresponds to allowing 'Has' logical operator in $filter.
/// </summary>
Has = 0x200,
/// <summary>
/// A value that corresponds to allowing all logical operators in $filter.
/// </summary>
All = Or | And | Equal | NotEqual | GreaterThan | GreaterThanOrEqual | LessThan | LessThanOrEqual | Not
All = Or | And | Equal | NotEqual | GreaterThan | GreaterThanOrEqual | LessThan | LessThanOrEqual | Not | Has
}
}

Просмотреть файл

@ -400,8 +400,7 @@ namespace System.Web.Http.OData.Query.Expressions
}
else
{
Type sourceUnderlyingType = Nullable.GetUnderlyingType(source.Type) ?? source.Type;
if (sourceUnderlyingType.IsEnum)
if (TypeHelper.IsEnum(source.Type))
{
// we handle enum conversions ourselves
return source;
@ -1045,8 +1044,7 @@ namespace System.Web.Http.OData.Query.Expressions
if (isNonstandardEdmPrimitive)
{
Type sourceType = source.Type;
sourceType = Nullable.GetUnderlyingType(sourceType) ?? sourceType;
Type sourceType = TypeHelper.GetUnderlyingTypeOrSelf(source.Type);
Contract.Assert(sourceType != conversionType);

Просмотреть файл

@ -159,6 +159,7 @@ namespace System.Web.Http.OData.Query.Validators
case BinaryOperatorKind.LessThan:
case BinaryOperatorKind.LessThanOrEqual:
case BinaryOperatorKind.Or:
case BinaryOperatorKind.Has:
// binary logical operators
ValidateLogicalOperator(binaryOperatorNode, settings);
break;
@ -288,6 +289,31 @@ namespace System.Web.Http.OData.Query.Validators
ValidateQueryNode(convertNode.Source, settings);
}
/// <summary>
/// Override this method for the enum node.
/// </summary>
/// <remarks>
/// This method is intended to be called from method overrides in subclasses.
/// This method also supports unit-testing scenarios and is not intended to be called from user code.
/// Call the Validate method to validate a <see cref="FilterQueryOption"/> instance.
/// </remarks>
/// <param name="enumNode"></param>
/// <param name="settings"></param>
public virtual void ValidateEnumNode(EnumNode enumNode, ODataValidationSettings settings)
{
if (enumNode == null)
{
throw Error.ArgumentNull("enumNode");
}
if (settings == null)
{
throw Error.ArgumentNull("settings");
}
// no default validation logic here
}
/// <summary>
/// Override this method for the navigation property node.
/// </summary>
@ -575,6 +601,14 @@ namespace System.Web.Http.OData.Query.Validators
/// <param name="settings"></param>
private void ValidateSingleValueNode(SingleValueNode node, ODataValidationSettings settings)
{
// TODO 1577: remove this and add it to switch, once the ODataLib v4 EnumNode.Kind bug is fixed.
EnumNode enumNode = node as EnumNode;
if (enumNode != null)
{
ValidateEnumNode(enumNode, settings);
return;
}
switch (node.Kind)
{
case QueryNodeKind.BinaryOperator:
@ -779,6 +813,10 @@ namespace System.Web.Http.OData.Query.Validators
result = AllowedLogicalOperators.Or;
break;
case BinaryOperatorKind.Has:
result = AllowedLogicalOperators.Has;
break;
default:
// should never be here
Contract.Assert(false, "ToLogicalOperator should never be here.");

Просмотреть файл

@ -170,6 +170,9 @@ namespace System.Web.Http.OData.Routing
case EdmTypeKind.Primitive:
return ParseAtPrimitiveProperty(model, previous, previousEdmType, segment, segments);
case EdmTypeKind.Enum:
return ParseAtEnumProperty(model, previous, previousEdmType, segment, segments);
default:
throw new ODataException(Error.Format(SRResources.InvalidPathSegment, segment, previous));
}
@ -395,6 +398,39 @@ namespace System.Web.Http.OData.Routing
throw new ODataException(Error.Format(SRResources.NoActionFoundForCollection, segment, collectionType.ElementType));
}
/// <summary>
/// Parses the next OData path segment following an enum property.
/// </summary>
/// <param name="model">The model to use for path parsing.</param>
/// <param name="previous">The previous path segment.</param>
/// <param name="previousEdmType">The EDM type of the OData path up to the previous segment.</param>
/// <param name="segment">The value of the segment to parse.</param>
/// <param name="segments">The queue of pending segments.</param>
/// <returns>A parsed representation of the segment.</returns>
protected virtual ODataPathSegment ParseAtEnumProperty(IEdmModel model, ODataPathSegment previous,
IEdmType previousEdmType, string segment, Queue<string> segments)
{
if (previous == null)
{
throw Error.ArgumentNull("previous");
}
if (segments == null)
{
throw Error.ArgumentNull("segments");
}
if (String.IsNullOrEmpty(segment))
{
throw Error.Argument(SRResources.SegmentNullOrEmpty);
}
if (segment == ODataSegmentKinds.Value)
{
return new ValuePathSegment();
}
throw new ODataException(Error.Format(SRResources.InvalidPathSegment, segment, previous));
}
/// <summary>
/// Parses the next OData path segment following a primitive property.
/// </summary>

Просмотреть файл

@ -475,7 +475,7 @@ namespace System.Web.Http.OData.Properties {
}
/// <summary>
/// Looks up a localized string similar to This service doesn&apos;t support OData requests in the form &apos;{0}&apos;..
/// Looks up a localized string similar to This service does not support OData requests in the form &apos;{0}&apos;..
/// </summary>
internal static string EntitySetControllerUnmappedRequest {
get {
@ -655,11 +655,11 @@ namespace System.Web.Http.OData.Properties {
}
/// <summary>
/// Looks up a localized string similar to The enum type &apos;{0}&apos; doesn&apos;t exist..
/// Looks up a localized string similar to The enum type &apos;{0}&apos; does not exist..
/// </summary>
internal static string EnumTypeNotExisting {
internal static string EnumTypeDoesNotExist {
get {
return ResourceManager.GetString("EnumTypeNotExisting", resourceCulture);
return ResourceManager.GetString("EnumTypeDoesNotExist", resourceCulture);
}
}
@ -925,7 +925,7 @@ namespace System.Web.Http.OData.Properties {
}
/// <summary>
/// Looks up a localized string similar to The provided mapping doesn&apos;t contain an entry for the entity type &apos;{0}&apos;..
/// Looks up a localized string similar to The provided mapping does not contain an entry for the entity type &apos;{0}&apos;..
/// </summary>
internal static string MappingDoesNotContainEntityType {
get {
@ -987,6 +987,15 @@ namespace System.Web.Http.OData.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to The binding value &apos;{0}&apos; cannot be bound to the enum type &apos;{1}&apos;..
/// </summary>
internal static string ModelBinderUtil_ValueCannotBeEnum {
get {
return ResourceManager.GetString("ModelBinderUtil_ValueCannotBeEnum", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The EDM model is missing on the read context. The model is required on the read context to deserialize the payload..
/// </summary>

Просмотреть файл

@ -228,8 +228,8 @@
<data name="EnumValueCannotBeLong" xml:space="preserve">
<value>The value of enum member '{0}' cannot be converted to a long type.</value>
</data>
<data name="EnumTypeNotExisting" xml:space="preserve">
<value>The enum type '{0}' doesn't exist.</value>
<data name="EnumTypeDoesNotExist" xml:space="preserve">
<value>The enum type '{0}' does not exist.</value>
</data>
<data name="RecursiveComplexTypesNotAllowed" xml:space="preserve">
<value>The complex type '{0}' has a reference to itself through the property '{1}'. A recursive loop of complex types is not allowed.</value>
@ -238,7 +238,7 @@
<value>'{0}' does not support Read.</value>
</data>
<data name="MappingDoesNotContainEntityType" xml:space="preserve">
<value>The provided mapping doesn't contain an entry for the entity type '{0}'.</value>
<value>The provided mapping does not contain an entry for the entity type '{0}'.</value>
</data>
<data name="NoIdLinkFactoryFound" xml:space="preserve">
<value>No IdLink factory was found. Try calling HasIdLink on the EntitySetConfiguration for '{0}'.</value>
@ -372,6 +372,9 @@
<data name="ValueIsInvalid" xml:space="preserve">
<value>The value '{0}' is invalid. {1}</value>
</data>
<data name="ModelBinderUtil_ValueCannotBeEnum" xml:space="preserve">
<value>The binding value '{0}' cannot be bound to the enum type '{1}'.</value>
</data>
<data name="ModelBinderUtil_ModelMetadataCannotBeNull" xml:space="preserve">
<value>The binding context cannot have a null ModelMetadata.</value>
</data>
@ -461,7 +464,7 @@
<comment>Language tag of the error messages according to RFC4646.</comment>
</data>
<data name="EntitySetControllerUnmappedRequest" xml:space="preserve">
<value>This service doesn't support OData requests in the form '{0}'.</value>
<value>This service does not support OData requests in the form '{0}'.</value>
</data>
<data name="EntitySetControllerUnmappedRequestErrorCode" xml:space="preserve">
<value>Not implemented.</value>
@ -609,7 +612,7 @@
</data>
<data name="EdmTypeCannotBeNull" xml:space="preserve">
<value>The EDM type of the object of type '{0}' is null. The EDM type of an {1} cannot be null.</value>
<comment>{1} may be System.Web.Http.OData.IEdmObject which doesn't scan. https://aspnetwebstack.codeplex.com/workitem/1558</comment>
<comment>{1} may be System.Web.Http.OData.IEdmObject which does not scan. https://aspnetwebstack.codeplex.com/workitem/1558</comment>
</data>
<data name="EdmObjectNull" xml:space="preserve">
<value>The property 'EdmObject' of {0} cannot be null.</value>

Просмотреть файл

@ -67,8 +67,10 @@
<Compile Include="OData\Builder\EnumPropertyConfiguration.cs" />
<Compile Include="OData\Builder\EnumTypeConfiguration.cs" />
<Compile Include="OData\Builder\EnumTypeConfigurationOfTEnumType.cs" />
<Compile Include="OData\Builder\NullableEnumTypeConfiguration.cs" />
<Compile Include="OData\Formatter\Deserialization\EnumDeserializationHelpers.cs" />
<Compile Include="OData\Formatter\Deserialization\ODataEnumDeserializer.cs" />
<Compile Include="OData\Formatter\ODataEnumValueMediaTypeMapping.cs" />
<Compile Include="OData\Formatter\Serialization\ODataEnumSerializer.cs" />
<Compile Include="OData\Query\NonFilterableAttribute.cs" />
<Compile Include="OData\Builder\Conventions\Attributes\NonFilterableAttributeEdmPropertyConvention.cs" />

Просмотреть файл

@ -72,6 +72,17 @@ namespace System.Web.Http
return false;
}
public static Type GetUnderlyingTypeOrSelf(Type type)
{
return Nullable.GetUnderlyingType(type) ?? type;
}
public static bool IsEnum(Type type)
{
Type underlyingTypeOrSelf = GetUnderlyingTypeOrSelf(type);
return underlyingTypeOrSelf.IsEnum;
}
/// <summary>
/// Determines whether the given type is a primitive type or
/// is a <see cref="string"/>, <see cref="DateTime"/>, <see cref="Decimal"/>,

Просмотреть файл

@ -61,9 +61,10 @@ namespace System.Web.Http.OData.Builder.Conventions
{ new DateTimeOffset(1,1,1,0,0,0,TimeSpan.Zero), "0001-01-01T00:00:00Z" },
{ TimeSpan.FromSeconds(86456), "duration'P1DT56S'" },
{ DateTimeOffset.FromFileTime(0).ToUniversalTime(), "1601-01-01T00:00:00Z" },
{ SimpleEnum.First, "SimpleEnum'First'" },
{ FlagsEnum.One | FlagsEnum.Two, "FlagsEnum'One, Two'" },
{ (SimpleEnum)123, "SimpleEnum'123'" },
{ SimpleEnum.First, "Microsoft.TestCommon.Types.SimpleEnum'First'" },
{ FlagsEnum.One | FlagsEnum.Two, "Microsoft.TestCommon.Types.FlagsEnum'One, Two'" },
{ (SimpleEnum)123, "Microsoft.TestCommon.Types.SimpleEnum'123'" },
{ (FlagsEnum)123, "Microsoft.TestCommon.Types.FlagsEnum'123'" },
};
}
}

Просмотреть файл

@ -26,7 +26,7 @@ namespace System.Web.Http.OData.Builder
color.Member(Color.Blue);
// Act
var model = builder.GetServiceModel();
var model = builder.GetEdmModel();
var colorType = model.SchemaElements.OfType<IEdmEnumType>().Single();
// Assert
@ -55,7 +55,7 @@ namespace System.Web.Http.OData.Builder
simple.Member(SimpleEnum.Third);
// Act
var model = builder.GetServiceModel();
var model = builder.GetEdmModel();
var colorType = model.SchemaElements.OfType<IEdmEnumType>().Single();
// Assert
@ -71,7 +71,7 @@ namespace System.Web.Http.OData.Builder
complexTypeConfiguration.EnumProperty(c => c.NullableColor);
// Act
var model = builder.GetServiceModel();
var model = builder.GetEdmModel();
var complexType = model.SchemaElements.OfType<IEdmStructuredType>().Single();
// Assert
@ -91,7 +91,7 @@ namespace System.Web.Http.OData.Builder
complexTypeConfiguration.CollectionProperty(c => c.Colors);
// Act
var model = builder.GetServiceModel();
var model = builder.GetEdmModel();
var complexType = model.SchemaElements.OfType<IEdmStructuredType>().Single();
// Assert
@ -111,7 +111,7 @@ namespace System.Web.Http.OData.Builder
complexTypeConfiguration.EnumProperty(c => c.RequiredColor);
// Act
var model = builder.GetServiceModel();
var model = builder.GetEdmModel();
var complexType = model.SchemaElements.OfType<IEdmStructuredType>().Single();
// Assert
@ -133,7 +133,7 @@ namespace System.Web.Http.OData.Builder
entityTypeConfiguration.EnumProperty(e => e.LongEnum);
// Act
var model = builder.GetServiceModel();
var model = builder.GetEdmModel();
var entityType = model.SchemaElements.OfType<IEdmStructuredType>().Single();
// Assert
@ -175,7 +175,7 @@ namespace System.Web.Http.OData.Builder
entityTypeConfiguration.EnumProperty(c => c.RequiredColor).IsOptional().IsConcurrencyToken();
// Act
var model = builder.GetServiceModel();
var model = builder.GetEdmModel();
var complexType = model.SchemaElements.OfType<IEdmStructuredType>().Single();
IEdmStructuralProperty requiredColor = complexType.Properties().SingleOrDefault(p => p.Name == "RequiredColor") as IEdmStructuralProperty;
@ -515,7 +515,7 @@ namespace System.Web.Http.OData.Builder
// Act & Assert
Assert.Throws<InvalidOperationException>(
() => builder.GetServiceModel(),
"The enum type 'Color' doesn't exist.");
"The enum type 'Color' does not exist.");
}
[Fact]
@ -529,88 +529,106 @@ namespace System.Web.Http.OData.Builder
// Act & Assert
Assert.Throws<InvalidOperationException>(
() => builder.GetServiceModel(),
"The enum type 'Color' doesn't exist.");
"The enum type 'Color' does not exist.");
}
[Fact]
public void ODataConventionModelBuilder_CreateEnumTypePropertyInComplexType()
public void ODataConventionModelBuilder_CreateRequiredEnumTypePropertyInComplexType()
{
// Arrange
var builder = new ODataConventionModelBuilder();
builder.ComplexType<ComplexTypeWithEnumTypePropertyTestModel>();
IEdmStructuredType complexType = AddComplexTypeWithODataConventionModelBuilder();
// Act & Assert
var model = builder.GetEdmModel();
var complexType = model.SchemaElements.OfType<IEdmStructuredType>().Single();
Assert.Equal(3, complexType.Properties().Count());
var requiredColor = complexType.Properties().SingleOrDefault(p => p.Name == "RequiredColor");
var nullableColor = complexType.Properties().SingleOrDefault(p => p.Name == "NullableColor");
var colors = complexType.Properties().SingleOrDefault(p => p.Name == "Colors");
Assert.NotNull(requiredColor);
Assert.NotNull(nullableColor);
Assert.NotNull(colors);
Assert.True(requiredColor.Type.IsEnum());
Assert.False(requiredColor.Type.IsNullable);
}
[Fact]
public void ODataConventionModelBuilder_CreateNullableEnumTypePropertyInComplexType()
{
// Arrange
IEdmStructuredType complexType = AddComplexTypeWithODataConventionModelBuilder();
// Act & Assert
Assert.Equal(3, complexType.Properties().Count());
var nullableColor = complexType.Properties().SingleOrDefault(p => p.Name == "NullableColor");
Assert.NotNull(nullableColor);
Assert.True(nullableColor.Type.IsEnum());
Assert.True(nullableColor.Type.IsNullable);
}
[Fact]
public void ODataConventionModelBuilder_CreateEnumTypeCollectionPropertyInComplexType()
{
// Arrange
IEdmStructuredType complexType = AddComplexTypeWithODataConventionModelBuilder();
// Act & Assert
Assert.Equal(3, complexType.Properties().Count());
var colors = complexType.Properties().SingleOrDefault(p => p.Name == "Colors");
Assert.NotNull(colors);
Assert.True(colors.Type.IsCollection());
Assert.True(((IEdmCollectionTypeReference)colors.Type).ElementType().IsEnum());
}
[Fact]
public void ODataConventionModelBuilder_CreateEnumTypePropertyInEntityType()
public void ODataConventionModelBuilder_CreateRequiredEnumTypePropertyInEntityType()
{
// Arrange
var builder = new ODataConventionModelBuilder();
builder.EntitySet<EntityTypeWithEnumTypePropertyTestModel>("Entities");
IEdmEntityType entityType = AddEntityTypeWithODataConventionModelBuilder();
// Act & Assert
var model = builder.GetEdmModel();
IEdmEntitySet entitySet = model.EntityContainers().Single().FindEntitySet("Entities");
Assert.NotNull(entitySet);
IEdmEntityType entityType = entitySet.ElementType;
Assert.Equal(6, entityType.Properties().Count());
Assert.Equal(5, entityType.Properties().Count());
var requiredColor = entityType.Properties().SingleOrDefault(p => p.Name == "RequiredColor");
var longEnum = entityType.Properties().SingleOrDefault(p => p.Name == "LongEnum");
var nullableColor = entityType.Properties().SingleOrDefault(p => p.Name == "NullableColor");
var colors = entityType.Properties().SingleOrDefault(p => p.Name == "Colors");
var complexTypeProperty = entityType.Properties().SingleOrDefault(p => p.Name == "ComplexType");
Assert.NotNull(requiredColor);
Assert.NotNull(longEnum);
Assert.NotNull(nullableColor);
Assert.NotNull(colors);
Assert.NotNull(complexTypeProperty);
Assert.True(requiredColor.Type.IsEnum());
Assert.False(requiredColor.Type.IsNullable);
}
[Fact]
public void ODataConventionModelBuilder_CreateNullableEnumTypePropertyInEntityType()
{
// Arrange
IEdmEntityType entityType = AddEntityTypeWithODataConventionModelBuilder();
// Act & Assert
Assert.Equal(5, entityType.Properties().Count());
var nullableColor = entityType.Properties().SingleOrDefault(p => p.Name == "NullableColor");
Assert.NotNull(nullableColor);
Assert.True(nullableColor.Type.IsEnum());
Assert.True(nullableColor.Type.IsNullable);
}
[Fact]
public void ODataConventionModelBuilder_CreateEnumTypeCollectionPropertyInEntityType()
{
// Arrange
IEdmEntityType entityType = AddEntityTypeWithODataConventionModelBuilder();
// Act & Assert
Assert.Equal(5, entityType.Properties().Count());
var colors = entityType.Properties().SingleOrDefault(p => p.Name == "Colors");
Assert.NotNull(colors);
Assert.True(colors.Type.IsCollection());
Assert.True(((IEdmCollectionTypeReference)colors.Type).ElementType().IsEnum());
}
[Fact]
public void ODataConventionModelBuilder_CreateLongEnumTypePropertyInEntityType()
{
// Arrange
IEdmEntityType entityType = AddEntityTypeWithODataConventionModelBuilder();
// Act & Assert
Assert.Equal(5, entityType.Properties().Count());
var longEnum = entityType.Properties().SingleOrDefault(p => p.Name == "LongEnum");
Assert.NotNull(longEnum);
Assert.True(longEnum.Type.IsEnum());
Assert.False(longEnum.Type.IsNullable);
Assert.True(EdmCoreModel.Instance.GetInt64(false).Definition == longEnum.Type.AsEnum().EnumDefinition().UnderlyingType);
Assert.True(nullableColor.Type.IsEnum());
Assert.True(nullableColor.Type.IsNullable);
Assert.True(colors.Type.IsCollection());
Assert.True(((IEdmCollectionTypeReference)colors.Type).ElementType().IsEnum());
Assert.True(complexTypeProperty.Type.IsComplex());
IEdmComplexType complexType = complexTypeProperty.Type.AsComplex().ComplexDefinition();
Assert.Equal(3, complexType.Properties().Count());
requiredColor = complexType.Properties().SingleOrDefault(p => p.Name == "RequiredColor");
nullableColor = complexType.Properties().SingleOrDefault(p => p.Name == "NullableColor");
colors = complexType.Properties().SingleOrDefault(p => p.Name == "Colors");
Assert.NotNull(requiredColor);
Assert.NotNull(nullableColor);
Assert.NotNull(colors);
Assert.True(requiredColor.Type.IsEnum());
Assert.False(requiredColor.Type.IsNullable);
Assert.True(nullableColor.Type.IsEnum());
Assert.True(nullableColor.Type.IsNullable);
Assert.True(colors.Type.IsCollection());
Assert.True(((IEdmCollectionTypeReference)colors.Type).ElementType().IsEnum());
Assert.Same(EdmCoreModel.Instance.GetInt64(false).Definition, longEnum.Type.AsEnum().EnumDefinition().UnderlyingType);
}
[Fact]
@ -632,6 +650,255 @@ namespace System.Web.Http.OData.Builder
// Act & Assert
Assert.Null(EdmLibHelpers.GetEdmPrimitiveTypeOrNull(clrType));
}
[Fact]
public void UnboundAction_ForEnumTypeInODataModelBuilder()
{
// Arrange
ODataModelBuilder builder = new ODataModelBuilder().Add_Color_EnumType();
ActionConfiguration actionConfiguration = builder.Action("UnboundAction");
actionConfiguration.Parameter<Color>("Color");
actionConfiguration.Returns<Color>();
// Act & Assert
IEdmModel model = builder.GetEdmModel();
IEdmActionImport actionImport = model.EntityContainers().Single().OperationImports().Single(o => o.Name == "UnboundAction") as IEdmActionImport;
IEdmTypeReference color = actionImport.Action.Parameters.Single(p => p.Name == "Color").Type;
IEdmTypeReference returnType = actionImport.Action.ReturnType;
IEdmEnumType colorType = model.SchemaElements.OfType<IEdmEnumType>().Single(e => e.Name == "Color");
Assert.False(color.IsNullable);
Assert.Same(colorType, color.Definition);
Assert.False(returnType.IsNullable);
Assert.Same(colorType, returnType.Definition);
}
[Fact]
public void UnboundFunction_ForEnumTypeInODataModelBuilder()
{
// Arrange
ODataModelBuilder builder = new ODataModelBuilder();
FunctionConfiguration functionConfiguration = builder.Function("UnboundFunction");
functionConfiguration.CollectionParameter<Color>("Colors");
functionConfiguration.ReturnsCollection<Color>();
builder.Add_Color_EnumType();
// Act & Assert
IEdmModel model = builder.GetEdmModel();
IEdmFunctionImport functionImport = model.EntityContainers().Single().OperationImports().Single(o => o.Name == "UnboundFunction") as IEdmFunctionImport;
IEdmTypeReference colors = functionImport.Function.Parameters.Single(p => p.Name == "Colors").Type;
IEdmTypeReference returnType = functionImport.Function.ReturnType;
IEdmEnumType colorType = model.SchemaElements.OfType<IEdmEnumType>().Single(e => e.Name == "Color");
Assert.True(colors.IsCollection());
Assert.Same(colorType, colors.AsCollection().ElementType().Definition);
Assert.True(returnType.IsCollection());
Assert.False(returnType.AsCollection().ElementType().IsNullable);
Assert.Same(colorType, returnType.AsCollection().ElementType().Definition);
}
[Fact]
public void BoundAction_ForEnumTypeInODataModelBuilder()
{
// Arrange
ODataModelBuilder builder = new ODataModelBuilder();
EntityTypeConfiguration<EnumModel> entityTypeConfiguration = builder.Entity<EnumModel>();
ActionConfiguration actionConfiguration = entityTypeConfiguration.Action("BoundAction");
actionConfiguration.CollectionParameter<Color>("Colors");
actionConfiguration.ReturnsCollection<Color?>();
builder.Add_Color_EnumType();
// Act & Assert
IEdmModel model = builder.GetEdmModel();
IEdmAction action = model.FindDeclaredOperations("Default.BoundAction").Single() as IEdmAction;
IEdmTypeReference colors = action.Parameters.Single(p => p.Name == "Colors").Type;
IEdmTypeReference returnType = action.ReturnType;
IEdmEnumType colorType = model.SchemaElements.OfType<IEdmEnumType>().Single(e => e.Name == "Color");
Assert.True(colors.IsCollection());
Assert.Same(colorType, colors.AsCollection().ElementType().Definition);
Assert.True(returnType.IsCollection());
Assert.True(returnType.AsCollection().ElementType().IsNullable);
Assert.Same(colorType, returnType.AsCollection().ElementType().Definition);
}
[Fact]
public void BoundFunction_ForEnumTypeInODataModelBuilder()
{
// Arrange
ODataModelBuilder builder = new ODataModelBuilder().Add_Color_EnumType();
EntityTypeConfiguration<EnumModel> entityTypeConfiguration = builder.Entity<EnumModel>();
FunctionConfiguration functionConfiguration = entityTypeConfiguration.Function("BoundFunction");
functionConfiguration.Parameter<Color?>("Color");
functionConfiguration.Returns<Color>();
// Act & Assert
IEdmModel model = builder.GetEdmModel();
IEdmFunction function = model.FindDeclaredOperations("Default.BoundFunction").Single() as IEdmFunction;
IEdmTypeReference color = function.Parameters.Single(p => p.Name == "Color").Type;
IEdmTypeReference returnType = function.ReturnType;
IEdmEnumType colorType = model.SchemaElements.OfType<IEdmEnumType>().Single(e => e.Name == "Color");
Assert.True(color.IsNullable);
Assert.Same(colorType, color.Definition);
Assert.Same(colorType, returnType.Definition);
}
[Fact]
public void UnboundAction_ForEnumTypeInODataConventionModelBuilder()
{
// Arrange
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
ActionConfiguration actionConfiguration = builder.Action("UnboundAction");
actionConfiguration.CollectionParameter<Color>("Colors");
actionConfiguration.Returns<Color?>();
// Act & Assert
IEdmModel model = builder.GetEdmModel();
IEdmActionImport actionImport = model.EntityContainers().Single().OperationImports().Single(o => o.Name == "UnboundAction") as IEdmActionImport;
IEdmTypeReference colors = actionImport.Action.Parameters.Single(p => p.Name == "Colors").Type;
IEdmTypeReference returnType = actionImport.Action.ReturnType;
IEdmEnumType colorType = model.SchemaElements.OfType<IEdmEnumType>().Single(e => e.Name == "Color");
Assert.True(colors.IsCollection());
Assert.False(colors.AsCollection().ElementType().IsNullable);
Assert.Same(colorType, colors.AsCollection().ElementType().Definition);
Assert.True(returnType.IsNullable);
Assert.Same(colorType, returnType.Definition);
}
[Fact]
public void UnboundFunction_ForEnumTypeInODataConventionModelBuilder()
{
// Arrange
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
FunctionConfiguration functionConfiguration = builder.Function("UnboundFunction");
functionConfiguration.Parameter<Color>("Color");
functionConfiguration.ReturnsCollection<Color>();
// Act & Assert
IEdmModel model = builder.GetEdmModel();
IEdmFunctionImport functionImport = model.EntityContainers().Single().OperationImports().Single(o => o.Name == "UnboundFunction") as IEdmFunctionImport;
IEdmTypeReference color = functionImport.Function.Parameters.Single(p => p.Name == "Color").Type;
IEdmTypeReference returnType = functionImport.Function.ReturnType;
IEdmEnumType colorType = model.SchemaElements.OfType<IEdmEnumType>().Single(e => e.Name == "Color");
Assert.Same(colorType, color.Definition);
Assert.True(returnType.IsCollection());
Assert.Same(colorType, returnType.AsCollection().ElementType().Definition);
}
[Fact]
public void BoundAction_ForEnumTypeInODataConventionModelBuilder()
{
// Arrange
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
EntityTypeConfiguration<EnumModel> entityTypeConfiguration = builder.Entity<EnumModel>();
ActionConfiguration actionConfiguration = entityTypeConfiguration.Action("BoundAction");
actionConfiguration.Parameter<Color>("Color");
actionConfiguration.ReturnsCollection<Color>();
// Act & Assert
IEdmModel model = builder.GetEdmModel();
IEdmAction action = model.FindDeclaredOperations("Default.BoundAction").Single() as IEdmAction;
IEdmTypeReference color = action.Parameters.Single(p => p.Name == "Color").Type;
IEdmTypeReference returnType = action.ReturnType;
IEdmEnumType colorType = model.SchemaElements.OfType<IEdmEnumType>().Single(e => e.Name == "Color");
Assert.Same(colorType, color.Definition);
Assert.True(returnType.IsCollection());
Assert.Same(colorType, returnType.AsCollection().ElementType().Definition);
}
[Fact]
public void BoundFunction_ForEnumTypeInODataConventionModelBuilder()
{
// Arrange
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
EntityTypeConfiguration<EnumModel> entityTypeConfiguration = builder.Entity<EnumModel>();
FunctionConfiguration functionConfiguration = entityTypeConfiguration.Function("BoundFunction");
functionConfiguration.CollectionParameter<Color?>("Colors");
functionConfiguration.Returns<Color>();
// Act & Assert
IEdmModel model = builder.GetEdmModel();
IEdmFunction function = model.FindDeclaredOperations("Default.BoundFunction").Single() as IEdmFunction;
IEdmTypeReference colors = function.Parameters.Single(p => p.Name == "Colors").Type;
IEdmTypeReference returnType = function.ReturnType;
IEdmEnumType colorType = model.SchemaElements.OfType<IEdmEnumType>().Single(e => e.Name == "Color");
Assert.True(colors.IsCollection());
Assert.True(colors.AsCollection().ElementType().IsNullable);
Assert.Same(colorType, colors.AsCollection().ElementType().Definition);
Assert.Same(colorType, returnType.Definition);
}
[Fact]
public void BoundFunction_ForEnumWithLongUnderlyingTypeInODataModelBuilder()
{
// Arrange
ODataModelBuilder builder = new ODataModelBuilder();
builder.Add_LongEnum_EnumType();
EntityTypeConfiguration<EnumModel> entityTypeConfiguration = builder.Entity<EnumModel>();
FunctionConfiguration functionConfiguration = entityTypeConfiguration.Function("BoundFunction");
functionConfiguration.Parameter<LongEnum>("LongEnum");
functionConfiguration.Returns<int>();
// Act & Assert
IEdmModel model = builder.GetEdmModel();
IEdmFunction function = model.FindDeclaredOperations("Default.BoundFunction").Single() as IEdmFunction;
IEdmTypeReference longEnumParameter = function.Parameters.Single(p => p.Name == "LongEnum").Type;
IEdmEnumType longEnumType = model.SchemaElements.OfType<IEdmEnumType>().Single(e => e.Name == "LongEnum");
Assert.Same(longEnumType, longEnumParameter.Definition);
Assert.Equal(EdmPrimitiveTypeKind.Int64, longEnumParameter.AsEnum().EnumDefinition().UnderlyingType.PrimitiveKind);
}
[Fact]
public void UnboundAction_ForEnumWithShortUnderlyingTypeInODataConventionModelBuilder()
{
// Arrange
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
ActionConfiguration actionConfiguration = builder.Action("UnboundAction");
actionConfiguration.Returns<ShortEnum>();
// Act & Assert
IEdmModel model = builder.GetEdmModel();
IEdmAction action = model.FindDeclaredOperations("Default.UnboundAction").Single() as IEdmAction;
IEdmTypeReference returnType = action.ReturnType;
IEdmEnumType shortEnumType = model.SchemaElements.OfType<IEdmEnumType>().Single(e => e.Name == "ShortEnum");
Assert.Same(shortEnumType, returnType.Definition);
Assert.Equal(EdmPrimitiveTypeKind.Int16, returnType.AsEnum().EnumDefinition().UnderlyingType.PrimitiveKind);
}
private IEdmStructuredType AddComplexTypeWithODataConventionModelBuilder()
{
var builder = new ODataConventionModelBuilder();
builder.ComplexType<ComplexTypeWithEnumTypePropertyTestModel>();
IEdmModel model = builder.GetEdmModel();
return model.SchemaElements.OfType<IEdmStructuredType>().Single();
}
private IEdmEntityType AddEntityTypeWithODataConventionModelBuilder()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<EntityTypeWithEnumTypePropertyTestModel>("Entities");
IEdmModel model = builder.GetEdmModel();
IEdmEntitySet entitySet = model.EntityContainers().Single().FindEntitySet("Entities");
return entitySet.ElementType;
}
}
public class ComplexTypeWithEnumTypePropertyTestModel
@ -648,7 +915,6 @@ namespace System.Web.Http.OData.Builder
public LongEnum LongEnum { get; set; }
public Color? NullableColor { get; set; }
public List<Color> Colors { get; set; }
public ComplexTypeWithEnumTypePropertyTestModel ComplexType { get; set; }
}
public abstract class BaseTypeWithEnumTypePropertyTestModel

Просмотреть файл

@ -173,7 +173,7 @@ namespace System.Web.Http.OData
EntityInstanceContext entityContext = new EntityInstanceContext { EntityType = entityType, EdmModel = model, EdmObject = instance };
Assert.Throws<InvalidOperationException>(
() => entityContext.EntityInstance, "The provided mapping doesn't contain an entry for the entity type 'NS.Name'.");
() => entityContext.EntityInstance, "The provided mapping does not contain an entry for the entity type 'NS.Name'.");
}
[Fact]

Просмотреть файл

@ -1,6 +1,7 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.ComponentModel.DataAnnotations;
using System.Web.Http.OData.Builder;
using System.Web.Http.OData.Builder.TestModels;
using System.Web.Http.OData.Formatter;
using System.Web.Http.OData.Formatter.Deserialization;
@ -107,6 +108,34 @@ namespace System.Web.Http.OData
"messageReader");
}
[Fact]
public void ODataEnumDeserializerRead_Throws_ForNullTypeParameter()
{
// Arrange
ODataMessageReader messageReader = new ODataMessageReader(new Mock<IODataRequestMessage>().Object);
Type type = null;
ODataDeserializerContext readContext = new ODataDeserializerContext();
// Act & Assert
Assert.ThrowsArgumentNull(
() => new ODataEnumDeserializer().Read(messageReader, type, readContext),
"type");
}
[Fact]
public void ODataEnumDeserializerRead_Throws_ForNullReadContextParameter()
{
// Arrange
ODataMessageReader messageReader = new ODataMessageReader(new Mock<IODataRequestMessage>().Object);
Type type = typeof(Color);
ODataDeserializerContext readContext = null;
// Act & Assert
Assert.ThrowsArgumentNull(
() => new ODataEnumDeserializer().Read(messageReader, type, readContext),
"readContext");
}
[Fact]
public void NullEnumValueDeserializerTest()
{
@ -117,23 +146,21 @@ namespace System.Web.Http.OData
{
Properties = new[]
{
new ODataProperty { Name = "RequiredColor", Value = "Red"},
new ODataProperty { Name = "NullableColor", Value = null}
},
TypeName = "TestModel.EnumComplex"
TypeName = "System.Web.Http.OData.EnumComplexWithNullableEnum"
};
IEdmModel model = GetEdmModel();
ODataDeserializerContext readContext = new ODataDeserializerContext() { Model = model };
IEdmComplexTypeReference enumComplexrTypeReference = model.GetEdmTypeReference(typeof(EnumComplex)).AsComplex();
IEdmComplexTypeReference enumComplexTypeReference = model.GetEdmTypeReference(typeof(EnumComplexWithNullableEnum)).AsComplex();
// Act
var enumComplex = deserializer.ReadComplexValue(complexValue, enumComplexrTypeReference, readContext) as EnumComplex;
var enumComplexWithNullableEnum = deserializer.ReadComplexValue(complexValue, enumComplexTypeReference, readContext) as EnumComplexWithNullableEnum;
// Assert
Assert.NotNull(enumComplex);
Assert.Equal(Color.Red, enumComplex.RequiredColor);
Assert.Null(enumComplex.NullableColor);
Assert.NotNull(enumComplexWithNullableEnum);
Assert.Null(enumComplexWithNullableEnum.NullableColor);
}
[Fact]
@ -146,49 +173,69 @@ namespace System.Web.Http.OData
{
Properties = new[]
{
new ODataProperty { Name = "RequiredColor", Value = (Color)123},
new ODataProperty { Name = "NullableColor", Value = Color.Green | Color.Blue}
new ODataProperty { Name = "RequiredColor", Value = (Color)123}
},
TypeName = "TestModel.EnumComplex"
TypeName = "System.Web.Http.OData.EnumComplexWithRequiredEnum"
};
IEdmModel model = GetEdmModel();
ODataDeserializerContext readContext = new ODataDeserializerContext() { Model = model };
IEdmComplexTypeReference enumComplexrTypeReference = model.GetEdmTypeReference(typeof(EnumComplex)).AsComplex();
IEdmComplexTypeReference enumComplexTypeReference = model.GetEdmTypeReference(typeof(EnumComplexWithRequiredEnum)).AsComplex();
// Act
var enumComplex = deserializer.ReadComplexValue(complexValue, enumComplexrTypeReference, readContext) as EnumComplex;
var enumComplexWithRequiredEnum = deserializer.ReadComplexValue(complexValue, enumComplexTypeReference, readContext) as EnumComplexWithRequiredEnum;
// Assert
Assert.NotNull(enumComplex);
Assert.Equal((Color)123, enumComplex.RequiredColor);
Assert.Equal(Color.Green | Color.Blue, enumComplex.NullableColor);
Assert.NotNull(enumComplexWithRequiredEnum);
Assert.Equal((Color)123, enumComplexWithRequiredEnum.RequiredColor);
}
[Theory]
[InlineData(Color.Red)]
[InlineData(Color.Green | Color.Blue)]
[InlineData((Color)1)]
[InlineData((Color)123)]
public void EnumValueDeserializerTest(Color color)
{
// Arrange
var deserializerProvider = new Mock<ODataDeserializerProvider>().Object;
var deserializer = new ODataComplexTypeDeserializer(deserializerProvider);
ODataComplexValue complexValue = new ODataComplexValue
{
Properties = new[]
{
new ODataProperty { Name = "RequiredColor", Value = color}
},
TypeName = "System.Web.Http.OData.EnumComplexWithRequiredEnum"
};
IEdmModel model = GetEdmModel();
ODataDeserializerContext readContext = new ODataDeserializerContext() { Model = model };
IEdmComplexTypeReference enumComplexTypeReference = model.GetEdmTypeReference(typeof(EnumComplexWithRequiredEnum)).AsComplex();
// Act
var enumComplexWithRequiredEnum = deserializer.ReadComplexValue(complexValue, enumComplexTypeReference, readContext) as EnumComplexWithRequiredEnum;
// Assert
Assert.NotNull(enumComplexWithRequiredEnum);
Assert.Equal(color, enumComplexWithRequiredEnum.RequiredColor);
}
private IEdmModel GetEdmModel()
{
EdmModel model = new EdmModel();
EdmEnumType color = new EdmEnumType("TestModel", "Color");
color.AddMember(new EdmEnumMember(color, "Red", new EdmIntegerConstant(1)));
color.AddMember(new EdmEnumMember(color, "Green", new EdmIntegerConstant(2)));
color.AddMember(new EdmEnumMember(color, "Blue", new EdmIntegerConstant(4)));
model.AddElement(color);
EdmComplexType enumComplex = new EdmComplexType("TestModel", "EnumComplex");
enumComplex.AddStructuralProperty("RequiredColor", color.ToEdmTypeReference(isNullable: false));
enumComplex.AddStructuralProperty("NullableColor", color.ToEdmTypeReference(isNullable: true));
model.AddElement(enumComplex);
model.SetAnnotationValue<ClrTypeAnnotation>(
model.FindDeclaredType("TestModel.EnumComplex"), new ClrTypeAnnotation(typeof(EnumComplex)));
return model;
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.ComplexType<EnumComplexWithRequiredEnum>();
builder.ComplexType<EnumComplexWithNullableEnum>();
return builder.GetEdmModel();
}
private class EnumComplex
private class EnumComplexWithRequiredEnum
{
public Color RequiredColor { get; set; }
}
private class EnumComplexWithNullableEnum
{
public Color? NullableColor { get; set; }
}
}

Просмотреть файл

@ -82,7 +82,7 @@ namespace System.Web.Http.OData
}
[Fact]
public void CreateODataValue_Throws_ForNotEnumTypeon()
public void CreateODataValue_Throws_ForNonEnumType()
{
// Arrange
object graph = null;
@ -109,6 +109,7 @@ namespace System.Web.Http.OData
private void EnumTypeSerializerTestForOData(string expectedContent, bool isJson)
{
// Arrange
ODataMediaTypeFormatter formatter = GetFormatter();
ObjectContent<EnumComplex> content = new ObjectContent<EnumComplex>(
new EnumComplex()
@ -119,6 +120,8 @@ namespace System.Web.Http.OData
},
formatter,
GetMediaType(isJson));
// Act & Assert
AssertEqual(isJson, expectedContent, content.ReadAsStringAsync().Result);
}

Просмотреть файл

@ -145,7 +145,7 @@ namespace System.Web.Http.OData.Formatter.Deserialization
{
Assert.Throws<InvalidOperationException>(
() => ODataComplexTypeDeserializer.CreateResource(_addressEdmType, new ODataDeserializerContext { Model = EdmCoreModel.Instance }),
"The provided mapping doesn't contain an entry for the entity type 'ODataDemo.Address'.");
"The provided mapping does not contain an entry for the entity type 'ODataDemo.Address'.");
}
[Fact]

Просмотреть файл

@ -312,7 +312,7 @@ namespace System.Web.Http.OData.Formatter.Deserialization
var deserializer = new ODataEntityDeserializer(_deserializerProvider);
Assert.Throws<ODataException>(
() => deserializer.CreateEntityResource(_productEdmType, new ODataDeserializerContext { Model = EdmCoreModel.Instance }),
"The provided mapping doesn't contain an entry for the entity type 'ODataDemo.Product'.");
"The provided mapping does not contain an entry for the entity type 'ODataDemo.Product'.");
}
[Fact]

Просмотреть файл

@ -11,6 +11,7 @@ using System.Web.Http.OData.Builder.TestModels;
using System.Web.Http.OData.Formatter.Deserialization;
using System.Web.Http.OData.Formatter.Serialization;
using System.Web.Http.Tracing;
using System.Xml;
using System.Xml.Linq;
using Microsoft.OData.Core;
using Microsoft.OData.Core.Atom;
@ -400,11 +401,13 @@ namespace System.Web.Http.OData.Formatter
using (HttpClient client = new HttpClient(host))
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/EnumCustomers"))
{
request.Content = new StringContent(string.Format(
@"{{'@odata.type':'#System.Web.Http.OData.Formatter.EnumCustomer','ID':0,'Color':'Green, Blue','Colors':['Red','Red, Blue']}}"));
request.Content = new StringContent(
string.Format(@"{{'@odata.type':'#System.Web.Http.OData.Formatter.EnumCustomer',
'ID':0,'Color':'Green, Blue','Colors':['Red','Red, Blue']}}"));
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
request.Headers.Accept.ParseAdd("application/json");
//Act
// Act
using (HttpResponseMessage response = client.SendAsync(request).Result)
{
// Assert
@ -421,6 +424,105 @@ namespace System.Web.Http.OData.Formatter
}
}
[Fact]
public void EnumSerializer_HasODataType_ForFullMetadata()
{
// Arrange & Act
string acceptHeader = "application/json;odata.metadata=full";
HttpResponseMessage response = GetEnumResponse(acceptHeader);
// Assert
response.EnsureSuccessStatusCode();
JObject customer = response.Content.ReadAsAsync<JObject>().Result;
Assert.Equal("#System.Web.Http.OData.Builder.TestModels.Color",
customer.GetValue("Color@odata.type"));
Assert.Equal("#Collection(System.Web.Http.OData.Builder.TestModels.Color)",
customer.GetValue("Colors@odata.type"));
}
[Theory]
[InlineData("application/json;odata.metadata=minimal")]
[InlineData("application/json;odata.metadata=none")]
public void EnumSerializer_HasNoODataType_ForNonFullMetadata(string acceptHeader)
{
// Arrange & Act
HttpResponseMessage response = GetEnumResponse(acceptHeader);
// Assert
response.EnsureSuccessStatusCode();
JObject customer = response.Content.ReadAsAsync<JObject>().Result;
Assert.False(customer.Values().Contains("Color@odata.type"));
Assert.False(customer.Values().Contains("Colors@odata.type"));
}
private HttpResponseMessage GetEnumResponse(string acceptHeader)
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<EnumCustomer>("EnumCustomers");
IEdmModel model = builder.GetEdmModel();
HttpConfiguration configuration = new HttpConfiguration();
configuration.Routes.MapODataRoute("odata", routePrefix: null, model: model);
HttpServer host = new HttpServer(configuration);
HttpClient client = new HttpClient(host);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/EnumCustomers");
request.Content = new StringContent(
string.Format(@"{{'@odata.type':'#System.Web.Http.OData.Formatter.EnumCustomer',
'ID':0,'Color':'Green, Blue','Colors':['Red','Red, Blue']}}"));
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
request.Headers.Accept.ParseAdd(acceptHeader);
HttpResponseMessage response = client.SendAsync(request).Result;
return response;
}
[Fact]
public void EnumSerializer_HasMetadataType_InAtom()
{
// Arrange
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<EnumCustomer>("EnumCustomers");
IEdmModel model = builder.GetEdmModel();
using (HttpConfiguration configuration = new HttpConfiguration())
{
configuration.Routes.MapODataRoute("odata", routePrefix: null, model: model);
using (HttpServer host = new HttpServer(configuration))
using (HttpClient client = new HttpClient(host))
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/EnumCustomers"))
{
request.Content = new StringContent(
string.Format(@"{{'@odata.type':'#System.Web.Http.OData.Formatter.EnumCustomer',
'ID':0,'Color':'Green, Blue','Colors':['Red','Red, Blue']}}"));
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
request.Headers.Accept.ParseAdd("application/atom+xml");
// Act
using (HttpResponseMessage response = client.SendAsync(request).Result)
{
// Assert
response.EnsureSuccessStatusCode();
var atomResult = response.Content.ReadAsStreamAsync().Result;
var atomXmlDocument = new XmlDocument();
atomXmlDocument.Load(atomResult);
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(atomXmlDocument.NameTable);
namespaceManager.AddNamespace("ns", atomXmlDocument.DocumentElement.NamespaceURI);
namespaceManager.AddNamespace("m", atomXmlDocument.DocumentElement.GetNamespaceOfPrefix("m"));
namespaceManager.AddNamespace("d", atomXmlDocument.DocumentElement.GetNamespaceOfPrefix("d"));
var colorMetadataType = atomXmlDocument.DocumentElement.SelectNodes(
"ns:content/m:properties/d:Color/attribute::m:type", namespaceManager).Cast<XmlNode>().Select(e => e.Value);
var colorsMetadataType = atomXmlDocument.DocumentElement.SelectNodes(
"ns:content/m:properties/d:Colors/attribute::m:type", namespaceManager).Cast<XmlNode>().Select(e => e.Value);
Assert.Equal("#System.Web.Http.OData.Builder.TestModels.Color", colorMetadataType.Single());
Assert.Equal("#Collection(System.Web.Http.OData.Builder.TestModels.Color)", colorsMetadataType.Single());
}
}
}
}
public class EnumCustomer
{
public int ID { get; set; }
@ -599,10 +701,6 @@ namespace System.Web.Http.OData.Formatter
public short Int16 { get; set; }
public FlagsEnum FlagsEnum { get; set; }
public List<FlagsEnum> FlagsEnums { get; set; }
public RelatedEntity Related { get; set; }
}

Просмотреть файл

@ -6,12 +6,17 @@ using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
using System.Web.Http.ModelBinding;
using System.Web.Http.OData.Builder;
using System.Web.Http.OData.Builder.Conventions;
using System.Web.Http.OData.Routing;
using System.Web.Http.OData.TestCommon;
using System.Web.Http.Routing;
using System.Web.Http.ValueProviders;
using Microsoft.OData.Core;
using Microsoft.OData.Core.UriParser;
using Microsoft.OData.Edm;
using Microsoft.TestCommon;
using Microsoft.TestCommon.Types;
@ -51,12 +56,11 @@ namespace System.Web.Http.OData.Formatter
{ (byte)1, "GetByte" },
{ "123", "GetString" },
{ Guid.Empty, "GetGuid" },
// TODO: Investigate how to add support for DataTime in webapi.odata, ODataLib v4 does not support it.
// TODO 1559: Investigate how to add support for DataTime in webapi.odata, ODataLib v4 does not support it.
{ TimeSpan.FromTicks(424242), "GetTimeSpan" },
{ DateTimeOffset.MaxValue, "GetDateTimeOffset" },
{ float.NaN, "GetFloat" },
// TODO: ODataLib v4 issue on decimal handling, bug filed.
//{ decimal.MaxValue, "GetDecimal" }
// TODO 1560: ODataLib v4 issue on decimal handling, bug filed.
};
}
}
@ -68,14 +72,14 @@ namespace System.Web.Http.OData.Formatter
return new TheoryDataSet<object, string>
{
{ "123", "GetBool" },
//{ 123, "GetDateTime" }, // v4 does not support DateTime
// TODO 1559: Investigate how to add support for DataTime in webapi.odata, ODataLib v4 does not support it.
{ "abc", "GetInt32" },
{ "abc", "GetGuid" },
{ "abc", "GetByte" },
{ "abc", "GetFloat" },
{ "abc", "GetDouble" },
{ "abc", "GetDecimal" },
//{ "abc", "GetDateTime" }, // v4 does not support DateTime
// TODO 1559: Investigate how to add support for DataTime in webapi.odata, ODataLib v4 does not support it.
{ "abc", "GetTimeSpan" },
{ "abc", "GetDateTimeOffset" },
{ -1, "GetUInt16"},
@ -201,6 +205,85 @@ namespace System.Web.Http.OData.Formatter
"name-2009",
response.Content.ReadAsAsync<string>().Result);
}
[Theory]
[InlineData(SimpleEnum.First, "GetEnum", "simpleEnum")]
[InlineData(FlagsEnum.One | FlagsEnum.Two, "GetFlagsEnum", "flagsEnum")]
[InlineData((SimpleEnum)12, "GetEnum", "simpleEnum")]
[InlineData((FlagsEnum)23, "GetFlagsEnum", "flagsEnum")]
public void ODataModelBinderProvider_Works_ForEnum(object value, string action, string parameterName)
{
// Arrange
HttpConfiguration configuration = new HttpConfiguration();
configuration.Services.Replace(typeof(ModelBinderProvider), new ODataModelBinderProvider());
configuration.Routes.MapODataRoute("odata", "", GetEdmModel())
.MapODataRouteAttributes(configuration);
var controllers = new[] { typeof(ODataModelBinderProviderTestODataController) };
TestAssemblyResolver resolver = new TestAssemblyResolver(new MockAssembly(controllers));
configuration.Services.Replace(typeof(IAssembliesResolver), resolver);
HttpServer server = new HttpServer(configuration);
HttpClient client = new HttpClient(server);
// Act
string url = String.Format(
"http://localhost/{0}({1}={2})",
action,
parameterName,
Uri.EscapeDataString(ConventionsHelpers.GetUriRepresentationForValue(value)));
HttpResponseMessage response = client.GetAsync(url).Result;
// Assert
response.EnsureSuccessStatusCode();
Assert.Equal(
value,
response.Content.ReadAsAsync(value.GetType(), configuration.Formatters).Result);
}
[Theory]
[InlineData("abc", "GetEnum", "simpleEnum")]
public void ODataModelBinderProvider_Throws_ForInvalidEnum(object value, string action, string parameterName)
{
// Arrange
HttpConfiguration configuration = new HttpConfiguration();
configuration.Services.Replace(typeof(ModelBinderProvider), new ODataModelBinderProvider());
configuration.Routes.MapODataRoute("odata", "", GetEdmModel())
.MapODataRouteAttributes(configuration);
var controllers = new[] { typeof(ODataModelBinderProviderTestODataController) };
TestAssemblyResolver resolver = new TestAssemblyResolver(new MockAssembly(controllers));
configuration.Services.Replace(typeof(IAssembliesResolver), resolver);
HttpServer server = new HttpServer(configuration);
HttpClient client = new HttpClient(server);
// Act
string url = String.Format(
"http://localhost/{0}({1}={2})",
action,
parameterName,
Uri.EscapeDataString(ConventionsHelpers.GetUriRepresentationForValue(value)));
HttpResponseMessage response = client.GetAsync(url).Result;
// Assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
private IEdmModel GetEdmModel()
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
FunctionConfiguration getEnum = builder.Function("GetEnum");
getEnum.Parameter<SimpleEnum>("simpleEnum");
getEnum.Returns<SimpleEnum>();
FunctionConfiguration getFlagsEnum = builder.Function("GetFlagsEnum");
getFlagsEnum.Parameter<FlagsEnum>("flagsEnum");
getFlagsEnum.Returns<FlagsEnum>();
return builder.GetEdmModel();
}
}
public class ODataKeyAttribute : ModelBinderAttribute
@ -312,13 +395,8 @@ namespace System.Web.Http.OData.Formatter
return id;
}
// TODO: Investigate how to add support for DataTime in webapi.odata, ODataLib v4 does not support it.
//public DateTime GetDateTime(DateTime id)
//{
// ThrowIfInsideThrowsController();
// return id;
//}
// TODO 1559: Investigate how to add support for DataTime in webapi.odata, ODataLib v4 does not support it.
public TimeSpan GetTimeSpan(TimeSpan id)
{
ThrowIfInsideThrowsController();
@ -393,4 +471,22 @@ namespace System.Web.Http.OData.Formatter
return name + "-" + model;
}
}
public class ODataModelBinderProviderTestODataController : ODataController
{
[HttpGet]
[ODataRoute("GetEnum(simpleEnum={simpleEnum})")]
public SimpleEnum GetEnum(SimpleEnum simpleEnum)
{
return simpleEnum;
}
[HttpGet]
[ODataRoute("GetFlagsEnum(flagsEnum={flagsEnum})")]
public FlagsEnum GetFlagsEnum(FlagsEnum flagsEnum)
{
return flagsEnum;
}
}
}

Просмотреть файл

@ -2,6 +2,7 @@
using System.Net.Http;
using System.Web.Http.OData.Builder;
using System.Web.Http.OData.Builder.TestModels;
using System.Web.Http.OData.Routing;
using Microsoft.OData.Edm;
using Microsoft.TestCommon;
@ -10,11 +11,24 @@ namespace System.Web.Http.OData.Formatter
{
public class ODataRawValueMediaTypeMappingTests
{
[Fact]
public void TryMatchMediaTypeWithPrimitiveRawValueThrowsArgumentNullWhenRequestIsNull()
public static TheoryDataSet<ODataRawValueMediaTypeMapping> ODataRawValueMediaTypeMappings
{
ODataPrimitiveValueMediaTypeMapping mapping = new ODataPrimitiveValueMediaTypeMapping();
get
{
return new TheoryDataSet<ODataRawValueMediaTypeMapping>
{
new ODataPrimitiveValueMediaTypeMapping(),
new ODataBinaryValueMediaTypeMapping(),
new ODataEnumValueMediaTypeMapping()
};
}
}
[Theory]
[PropertyData("ODataRawValueMediaTypeMappings")]
public void TryMatchMediaType_ThrowsArgumentNull_WhenRequestIsNull(ODataRawValueMediaTypeMapping mapping)
{
// Arrange, Act & Assert
Assert.ThrowsArgumentNull(() => { mapping.TryMatchMediaType(null); }, "request");
}
@ -34,14 +48,17 @@ namespace System.Web.Http.OData.Formatter
Assert.Equal(1.0, mapResult);
}
[Fact]
public void TryMatchMediaTypeWithNonODataRequestDoesntMatchRequest()
[Theory]
[PropertyData("ODataRawValueMediaTypeMappings")]
public void TryMatchMediaType_DoesntMatchRequest_WithNonODataRequest(ODataRawValueMediaTypeMapping mapping)
{
ODataPrimitiveValueMediaTypeMapping mapping = new ODataPrimitiveValueMediaTypeMapping();
// Arrange
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/");
// Act
double mapResult = mapping.TryMatchMediaType(request);
// Assert
Assert.Equal(0, mapResult);
}
@ -61,6 +78,44 @@ namespace System.Web.Http.OData.Formatter
Assert.Equal(0, mapResult);
}
[Fact]
public void TryMatchMediaType_MatchesRequest_WithEnumRawValue()
{
// Arrange
IEdmModel model = GetEnumModel();
PropertyAccessPathSegment propertySegment = new PropertyAccessPathSegment((model.GetEdmType(typeof(EnumEntity)) as IEdmEntityType).FindProperty("EnumProperty"));
ODataPath path = new ODataPath(new EntitySetPathSegment("EnumEntity"), new KeyValuePathSegment("1"), propertySegment, new ValuePathSegment());
ODataEnumValueMediaTypeMapping mapping = new ODataEnumValueMediaTypeMapping();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/EnumEntity(1)/EnumProperty/$value");
request.SetEdmModel(model);
request.SetODataPath(path);
// Act
double mapResult = mapping.TryMatchMediaType(request);
// Assert
Assert.Equal(1.0, mapResult);
}
[Fact]
public void TryMatchMediaType_DoesnotMatchRequest_ODataEnumValueMediaTypeMappingWithNonRawvalueRequest()
{
// Arrange
IEdmModel model = GetEnumModel();
PropertyAccessPathSegment propertySegment = new PropertyAccessPathSegment((model.GetEdmType(typeof(EnumEntity)) as IEdmEntityType).FindProperty("EnumProperty"));
ODataPath path = new ODataPath(new EntitySetPathSegment("EnumEntity"), new KeyValuePathSegment("1"), propertySegment);
ODataEnumValueMediaTypeMapping mapping = new ODataEnumValueMediaTypeMapping();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/EnumEntity(1)/EnumProperty/");
request.SetEdmModel(model);
request.SetODataPath(path);
// Act
double mapResult = mapping.TryMatchMediaType(request);
// Assert
Assert.Equal(0, mapResult);
}
[Fact]
public void TryMatchMediaTypeWithBinaryRawValueMatchesRequest()
{
@ -89,5 +144,18 @@ namespace System.Web.Http.OData.Formatter
builder.EntitySet<RawValueEntity>("RawValue");
return builder.GetEdmModel();
}
private class EnumEntity
{
public int Id { get; set; }
public Color EnumProperty { get; set; }
}
private static IEdmModel GetEnumModel()
{
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<EnumEntity>("EnumEntity");
return builder.GetEdmModel();
}
}
}

Просмотреть файл

@ -7,6 +7,7 @@ using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.RegularExpressions;
using System.Web.Http.OData.Builder;
using System.Web.Http.OData.Builder.TestModels;
using System.Web.Http.OData.Formatter.Deserialization;
using System.Web.Http.OData.Formatter.Serialization;
using System.Web.Http.OData.Routing;
@ -68,6 +69,11 @@ namespace System.Web.Http.OData.Formatter
{
ODataModelBuilder model = new ODataModelBuilder();
var color = model.EnumType<Color>();
color.Member(Color.Red);
color.Member(Color.Green);
color.Member(Color.Blue);
var people = model.EntitySet<FormatterPerson>("People");
people.HasFeedSelfLink(context => new Uri(context.Url.ODataLink(new EntitySetPathSegment(
context.EntitySet))));
@ -84,6 +90,7 @@ namespace System.Web.Http.OData.Formatter
person.Property(p => p.Age);
person.Property(p => p.MyGuid);
person.Property(p => p.Name);
person.EnumProperty(p => p.FavoriteColor);
person.ComplexProperty<FormatterOrder>(p => p.Order);
var order = model.ComplexType<FormatterOrder>();
@ -122,6 +129,7 @@ namespace System.Web.Http.OData.Formatter
public Guid MyGuid { get; set; }
public string Name { get; set; }
public FormatterOrder Order { get; set; }
public Color FavoriteColor { get; set; }
[Key]
public int PerId { get; set; }
}

Просмотреть файл

@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Web.Http.OData.Builder.TestModels;
namespace System.Web.Http.OData.Formatter
{
@ -21,7 +22,7 @@ namespace System.Web.Http.OData.Formatter
public FormatterPerson GetFormatterPerson(int key)
{
FormatterPerson obj = new FormatterPerson() { MyGuid = new Guid("f99080c0-2f9e-472e-8c72-1a8ecd9f902d"), PerId = key, Age = 10, Name = "Asha", Order = new FormatterOrder() { OrderName = "FirstOrder", OrderAmount = 235342 } };
FormatterPerson obj = new FormatterPerson() { MyGuid = new Guid("f99080c0-2f9e-472e-8c72-1a8ecd9f902d"), PerId = key, Age = 10, Name = "Asha", Order = new FormatterOrder() { OrderName = "FirstOrder", OrderAmount = 235342 }, FavoriteColor = Color.Red | Color.Green };
return obj;
}

Просмотреть файл

@ -8,6 +8,7 @@ using System.Web.Http.OData.Routing;
using Microsoft.OData.Core;
using Microsoft.OData.Edm;
using Microsoft.OData.Edm.Library;
using Microsoft.OData.Edm.Library.Values;
using Microsoft.TestCommon;
using Moq;
@ -26,8 +27,7 @@ namespace System.Web.Http.OData.Formatter.Serialization
{ typeof(byte[]), EdmPrimitiveTypeKind.Binary },
{ typeof(bool), EdmPrimitiveTypeKind.Boolean },
{ typeof(byte), EdmPrimitiveTypeKind.Byte },
// TODO: Investigate how to add support for DataTime in webapi.odata, ODataLib v4 does not support it.
// { typeof(DateTime), EdmPrimitiveTypeKind.DateTimeOffset },
// TODO 1559: Investigate how to add support for DataTime in webapi.odata, ODataLib v4 does not support it.
{ typeof(DateTimeOffset), EdmPrimitiveTypeKind.DateTimeOffset },
{ typeof(decimal), EdmPrimitiveTypeKind.Decimal },
{ typeof(double), EdmPrimitiveTypeKind.Double },
@ -104,6 +104,32 @@ namespace System.Web.Http.OData.Formatter.Serialization
Assert.Equal(ODataPayloadKind.Value, serializer.ODataPayloadKind);
}
[Fact]
public void GetODataSerializer_Enum()
{
var serializerProvider = new DefaultODataSerializerProvider();
HttpRequestMessage request = new HttpRequestMessage();
var serializer = serializerProvider.GetODataPayloadSerializer(GetEnumModel(), typeof(TestEnum), request);
Assert.NotNull(serializer);
var enumSerializer = Assert.IsType<ODataEnumSerializer>(serializer);
Assert.Equal(ODataPayloadKind.Property, enumSerializer.ODataPayloadKind);
}
[Fact]
public void GetODataPayloadSerializer_ReturnsRawValueSerializer_ForEnumValueRequests()
{
ODataSerializerProvider serializerProvider = new DefaultODataSerializerProvider();
HttpRequestMessage request = new HttpRequestMessage();
request.SetODataPath(new ODataPath(new ValuePathSegment()));
var serializer = serializerProvider.GetODataPayloadSerializer(GetEnumModel(), typeof(TestEnum), request);
Assert.NotNull(serializer);
var rawValueSerializer = Assert.IsType<ODataRawValueSerializer>(serializer);
Assert.Equal(ODataPayloadKind.Value, rawValueSerializer.ODataPayloadKind);
}
[Fact]
public void GetODataSerializer_Entity()
{
@ -231,6 +257,20 @@ namespace System.Web.Http.OData.Formatter.Serialization
Assert.Same(instance1, instance2);
}
private static IEdmModel GetEnumModel()
{
EdmModel model = new EdmModel();
EdmEnumType enumType = new EdmEnumType("TestModel", "TestEnum");
enumType.AddMember(new EdmEnumMember(enumType, "FirstValue", new EdmIntegerConstant(0)));
enumType.AddMember(new EdmEnumMember(enumType, "FirstValue", new EdmIntegerConstant(1)));
model.AddElement(enumType);
model.SetAnnotationValue(model.FindDeclaredType("TestModel.TestEnum"), new ClrTypeAnnotation(typeof(TestEnum)));
return model;
}
private enum TestEnum
{
FirstValue,

Просмотреть файл

@ -0,0 +1,64 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using Microsoft.OData.Core;
using Microsoft.OData.Edm;
using Microsoft.OData.Edm.Library;
using Microsoft.TestCommon;
namespace System.Web.Http.OData.Formatter.Serialization
{
public class ODataEnumTypeSerializerTests
{
[Theory]
[InlineData(ODataMetadataLevel.Default)]
[InlineData(ODataMetadataLevel.MinimalMetadata)]
public void AddTypeNameAnnotationAsNeeded_DoesNotAddAnnotation(ODataMetadataLevel metadataLevel)
{
// Arrange
ODataEnumValue enumValue = new ODataEnumValue("value");
IEdmEnumTypeReference enumType = new EdmEnumTypeReference(
new EdmEnumType("TestModel", "EnumType"), isNullable: false);
// Act
ODataEnumSerializer.AddTypeNameAnnotationAsNeeded(enumValue, enumType, metadataLevel);
// Assert
SerializationTypeNameAnnotation annotation = enumValue.GetAnnotation<SerializationTypeNameAnnotation>();
Assert.Null(annotation);
}
[Fact]
public void AddTypeNameAnnotationAsNeeded_AddAnnotation_InFullMetadataMode()
{
// Arrange
ODataEnumValue enumValue = new ODataEnumValue("value");
IEdmEnumTypeReference enumType = new EdmEnumTypeReference(
new EdmEnumType("TestModel", "EnumType"), isNullable: false);
// Act
ODataEnumSerializer.AddTypeNameAnnotationAsNeeded(enumValue, enumType, ODataMetadataLevel.FullMetadata);
// Assert
SerializationTypeNameAnnotation annotation = enumValue.GetAnnotation<SerializationTypeNameAnnotation>();
Assert.NotNull(annotation);
Assert.Equal("TestModel.EnumType", annotation.TypeName);
}
[Fact]
public void AddTypeNameAnnotationAsNeeded_AddsNullAnnotation_InNoMetadataMode()
{
// Arrange
ODataEnumValue enumValue = new ODataEnumValue("value");
IEdmEnumTypeReference enumType = new EdmEnumTypeReference(
new EdmEnumType("TestModel", "EnumType"), isNullable: false);
// Act
ODataEnumSerializer.AddTypeNameAnnotationAsNeeded(enumValue, enumType, ODataMetadataLevel.NoMetadata);
// Assert
SerializationTypeNameAnnotation annotation = enumValue.GetAnnotation<SerializationTypeNameAnnotation>();
Assert.NotNull(annotation);
Assert.Null(annotation.TypeName);
}
}
}

Просмотреть файл

@ -1,6 +1,7 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.IO;
using System.Web.Http.OData.Builder.TestModels;
using Microsoft.OData.Core;
using Microsoft.TestCommon;
using Moq;
@ -50,5 +51,23 @@ namespace System.Web.Http.OData.Formatter.Serialization
Assert.Equal(value.ToString(), reader.ReadToEnd());
}
[Fact]
public void SerializesEnumType()
{
ODataRawValueSerializer serializer = new ODataRawValueSerializer();
Mock<IODataRequestMessage> mockRequest = new Mock<IODataRequestMessage>();
Stream stream = new MemoryStream();
mockRequest.Setup(r => r.GetStream()).Returns(stream);
ODataMessageWriter messageWriter = new ODataMessageWriter(mockRequest.Object);
object value = Color.Red | Color.Blue;
serializer.WriteObject(value, value.GetType(), messageWriter, null);
stream.Seek(0, SeekOrigin.Begin);
TextReader reader = new StreamReader(stream);
string result = reader.ReadToEnd();
Assert.Equal(value.ToString(), result, ignoreCase: true);
}
}
}

Просмотреть файл

@ -385,7 +385,7 @@ namespace System.Web.Http.OData.Query.Expressions
Assert.Throws<ODataException>(
() => _binder.CreatePropertyNameExpression(_model.Customer, specialOrdersProperty, customer),
"The provided mapping doesn't contain an entry for the entity type 'NS.SpecialCustomer'.");
"The provided mapping does not contain an entry for the entity type 'NS.SpecialCustomer'.");
}
[Fact]
@ -421,7 +421,7 @@ namespace System.Web.Http.OData.Query.Expressions
Assert.Throws<ODataException>(
() => _binder.CreatePropertyValueExpression(_model.Customer, specialOrdersProperty, customer),
"The provided mapping doesn't contain an entry for the entity type 'NS.SpecialCustomer'.");
"The provided mapping does not contain an entry for the entity type 'NS.SpecialCustomer'.");
}
[Fact]
@ -506,7 +506,7 @@ namespace System.Web.Http.OData.Query.Expressions
// Act & Assert
Assert.Throws<ODataException>(
() => SelectExpandBinder.CreateTypeNameExpression(source, baseType, model),
"The provided mapping doesn't contain an entry for the entity type 'NS.DerivedType'.");
"The provided mapping does not contain an entry for the entity type 'NS.DerivedType'.");
}
[Fact]

Просмотреть файл

@ -492,6 +492,29 @@ namespace System.Web.Http.OData.Query
actualCustomers.Select(enumModel => enumModel.Id));
}
[Theory]
[InlineData("Simple has null", typeof(ODataException))]
[InlineData("null has Microsoft.TestCommon.Types.SimpleEnum'First'", typeof(ODataException))]
[InlineData("Id has Microsoft.TestCommon.Types.SimpleEnum'First'", typeof(ODataException))]
[InlineData("null has null", typeof(NotSupportedException))]
[InlineData("Simple has 23", typeof(ODataException))]
[InlineData("'Some string' has 0", typeof(ODataException))]
public void ApplyToEnums_Throws_WithInvalidFilter(string filter, Type exceptionType)
{
// Arrange
var model = GetEnumModel();
var context = new ODataQueryContext(model, typeof(EnumModel));
var filterOption = new FilterQueryOption(filter, context);
IEnumerable<EnumModel> enumModels = EnumModelTestData;
// Act & Assert
Assert.Throws(
exceptionType,
() => filterOption.ApplyTo(
enumModels.AsQueryable(),
new ODataQuerySettings { HandleNullPropagation = HandleNullPropagationOption.True }));
}
[Theory]
[InlineData(
"Simple eq Microsoft.TestCommon.Types.SimpleEnum'4'",

Просмотреть файл

@ -5,6 +5,7 @@ using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http.OData.Builder.TestModels;
using Microsoft.OData.Core;
namespace System.Web.Http.OData.Query
@ -192,6 +193,7 @@ namespace System.Web.Http.OData.Query
public byte[] Image { get; set; }
public DateTimeOffset Birthday { get; set; }
public double AmountSpent { get; set; }
public Color FavoriteColor { get; set; }
public QueryCompositionAddress NavigationWithNonFilterableProperty { get; set; }

Просмотреть файл

@ -108,6 +108,19 @@ namespace System.Web.Http.OData.Query.Validators
"Arithmetic operator 'Modulo' is not allowed. To allow it, set the 'AllowedArithmeticOperators' property on QueryableAttribute or QueryValidationSettings.");
}
[Fact]
public void ValidateThrowsIfHasIsNotAllowed()
{
Assert.DoesNotThrow(() =>
_validator.Validate(new FilterQueryOption("FavoriteColor has System.Web.Http.OData.Builder.TestModels.Color'Red'", _context),
new ODataValidationSettings() { AllowedLogicalOperators = AllowedLogicalOperators.All }));
Assert.Throws<ODataException>(() =>
_validator.Validate(new FilterQueryOption("FavoriteColor has System.Web.Http.OData.Builder.TestModels.Color'Red'", _context),
new ODataValidationSettings() { AllowedLogicalOperators = AllowedLogicalOperators.Equal }),
"Logical operator 'Has' is not allowed. To allow it, set the 'AllowedLogicalOperators' property on QueryableAttribute or QueryValidationSettings.");
}
// want to test if all the virtual methods are being invoked correctly
[Fact]
public void ValidateVisitAll()
@ -318,6 +331,25 @@ namespace System.Web.Http.OData.Query.Validators
Assert.Equal(1, _validator.Times["ValidateParameterQueryNode"]); // $it
}
[Fact]
public void ValidateVisitLogicalOperatorHas()
{
// Arrange
FilterQueryOption option = new FilterQueryOption("FavoriteColor has System.Web.Http.OData.Builder.TestModels.Color'Red'", _context);
// Act
_validator.Validate(option, _settings);
// Assert
Assert.Equal(6, _validator.Times.Keys.Count);
Assert.Equal(1, _validator.Times["Validate"]); // entry
Assert.Equal(1, _validator.Times["ValidateSingleValuePropertyAccessNode"]); // FavouriteColor
Assert.Equal(1, _validator.Times["ValidateLogicalOperator"]); // has
Assert.Equal(1, _validator.Times["ValidateEnumQueryNode"]); // Red
Assert.Equal(1, _validator.Times["ValidateBinaryOperatorQueryNode"]); // has
Assert.Equal(1, _validator.Times["ValidateParameterQueryNode"]); // $it
}
[Theory]
[InlineData("Id eq 1")]
[InlineData("Id ne 1")]
@ -353,6 +385,7 @@ namespace System.Web.Http.OData.Query.Validators
[InlineData("Tags/all(t : t eq '1')")]
[InlineData("System.Web.Http.OData.Query.QueryCompositionCustomerBase/Id eq 1")]
[InlineData("Contacts/System.Web.Http.OData.Query.QueryCompositionCustomerBase/any()")]
[InlineData("FavoriteColor has System.Web.Http.OData.Builder.TestModels.Color'Red'")]
public void Validator_Doesnot_Throw_For_ValidQueries(string filter)
{
// Arrange
@ -430,6 +463,12 @@ namespace System.Web.Http.OData.Query.Validators
base.ValidateConvertNode(convertQueryNode, settings);
}
public override void ValidateEnumNode(EnumNode enumNode, ODataValidationSettings settings)
{
IncrementCount("ValidateEnumQueryNode");
base.ValidateEnumNode(enumNode, settings);
}
public override void ValidateLogicalOperator(BinaryOperatorNode binaryNode, ODataValidationSettings settings)
{
IncrementCount("ValidateLogicalOperator");

Просмотреть файл

@ -97,6 +97,8 @@ namespace System.Web.Http.OData.Routing
[InlineData("RoutingCustomers(112)/GetRelatedRoutingCustomers", "~/entityset/key/action")]
[InlineData("RoutingCustomers/System.Web.Http.OData.Routing.VIP/GetMostProfitable", "~/entityset/cast/action")]
[InlineData("Products(1)/RoutingCustomers/System.Web.Http.OData.Routing.VIP(1)/RelationshipManager/ManagedProducts", "~/entityset/key/navigation/cast/key/navigation/navigation")]
[InlineData("EnumCustomers(1)/Color", "~/entityset/key/property")]
[InlineData("EnumCustomers(1)/Color/$value", "~/entityset/key/property/$value")]
public void Parse_ReturnsPath_WithCorrectTemplate(string odataPath, string template)
{
ODataPath path = _parser.Parse(_model, odataPath);

Просмотреть файл

@ -5,6 +5,7 @@ using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web.Http.Dispatcher;
using System.Web.Http.OData.Builder;
using System.Web.Http.OData.Builder.TestModels;
using System.Web.Http.OData.TestCommon;
using Microsoft.OData.Edm;
@ -22,6 +23,7 @@ namespace System.Web.Http.OData.Routing
builder.EntitySet<SalesPerson>("SalesPeople");
builder.EntitySet<EmailAddress>("EmailAddresses");
builder.EntitySet<üCategory>("üCategories");
builder.EntitySet<EnumCustomer>("EnumCustomers");
ActionConfiguration getRoutingCustomerById = builder.Action("GetRoutingCustomerById");
getRoutingCustomerById.Parameter<int>("RoutingCustomerId");
@ -172,5 +174,11 @@ namespace System.Web.Http.OData.Routing
{
public int ID { get; set; }
}
public class EnumCustomer
{
public int ID { get; set; }
public Color Color { get; set; }
}
}
}

Просмотреть файл

@ -2,7 +2,7 @@
<m:value xmlns:d="http://docs.oasis-open.org/odata/ns/data" xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml" xmlns:m="http://docs.oasis-open.org/odata/ns/metadata">
<m:element m:type="#System.Web.Http.OData.TestCommon.Models.Person">
<Age m:type="Int32" xmlns="http://docs.oasis-open.org/odata/ns/data">20</Age>
<Gender xmlns="http://docs.oasis-open.org/odata/ns/data">Male</Gender>
<Gender m:type="#System.Web.Http.OData.TestCommon.Models.Gender" xmlns="http://docs.oasis-open.org/odata/ns/data">Male</Gender>
<FirstName xmlns="http://docs.oasis-open.org/odata/ns/data">Frank</FirstName>
<Alias m:type="#Collection(String)" xmlns="http://docs.oasis-open.org/odata/ns/data">
<m:element>Alias0</m:element>
@ -17,7 +17,7 @@
<CountryCode m:type="Int32">1</CountryCode>
<AreaCode m:type="Int32">425</AreaCode>
<Number m:type="Int32">9879089</Number>
<PhoneType>HomePhone</PhoneType>
<PhoneType m:type="#System.Web.Http.OData.TestCommon.Models.PhoneType">HomePhone</PhoneType>
</HomeNumber>
<FavoriteHobby m:type="#System.Web.Http.OData.TestCommon.Models.IActivity" xmlns="http://docs.oasis-open.org/odata/ns/data">
<ActivityName>Xbox Gaming</ActivityName>
@ -25,7 +25,7 @@
</m:element>
<m:element m:type="#System.Web.Http.OData.TestCommon.Models.Person">
<Age m:type="Int32" xmlns="http://docs.oasis-open.org/odata/ns/data">21</Age>
<Gender xmlns="http://docs.oasis-open.org/odata/ns/data">Male</Gender>
<Gender m:type="#System.Web.Http.OData.TestCommon.Models.Gender" xmlns="http://docs.oasis-open.org/odata/ns/data">Male</Gender>
<FirstName xmlns="http://docs.oasis-open.org/odata/ns/data">Steve</FirstName>
<Alias m:type="#Collection(String)" xmlns="http://docs.oasis-open.org/odata/ns/data">
<m:element>Alias1</m:element>
@ -40,7 +40,7 @@
<CountryCode m:type="Int32">1</CountryCode>
<AreaCode m:type="Int32">425</AreaCode>
<Number m:type="Int32">9879090</Number>
<PhoneType>HomePhone</PhoneType>
<PhoneType m:type="#System.Web.Http.OData.TestCommon.Models.PhoneType">HomePhone</PhoneType>
</HomeNumber>
<FavoriteHobby m:type="#System.Web.Http.OData.TestCommon.Models.IActivity" xmlns="http://docs.oasis-open.org/odata/ns/data">
<ActivityName>Xbox Gaming</ActivityName>
@ -48,7 +48,7 @@
</m:element>
<m:element m:type="#System.Web.Http.OData.TestCommon.Models.Person">
<Age m:type="Int32" xmlns="http://docs.oasis-open.org/odata/ns/data">22</Age>
<Gender xmlns="http://docs.oasis-open.org/odata/ns/data">Male</Gender>
<Gender m:type="#System.Web.Http.OData.TestCommon.Models.Gender" xmlns="http://docs.oasis-open.org/odata/ns/data">Male</Gender>
<FirstName xmlns="http://docs.oasis-open.org/odata/ns/data">Tom</FirstName>
<Alias m:type="#Collection(String)" xmlns="http://docs.oasis-open.org/odata/ns/data">
<m:element>Alias2</m:element>
@ -63,7 +63,7 @@
<CountryCode m:type="Int32">1</CountryCode>
<AreaCode m:type="Int32">425</AreaCode>
<Number m:type="Int32">9879091</Number>
<PhoneType>HomePhone</PhoneType>
<PhoneType m:type="#System.Web.Http.OData.TestCommon.Models.PhoneType">HomePhone</PhoneType>
</HomeNumber>
<FavoriteHobby m:type="#System.Web.Http.OData.TestCommon.Models.IActivity" xmlns="http://docs.oasis-open.org/odata/ns/data">
<ActivityName>Xbox Gaming</ActivityName>

Просмотреть файл

@ -16,7 +16,7 @@
<m:properties>
<d:EmployeeId m:type="Int64">0</d:EmployeeId>
<d:Age m:type="Int32">20</d:Age>
<d:Gender>Male</d:Gender>
<d:Gender m:type="#System.Web.Http.OData.TestCommon.Models.Gender">Male</d:Gender>
<d:FirstName>Frank</d:FirstName>
<d:Alias m:type="#Collection(String)">
<m:element>Alias0</m:element>
@ -31,7 +31,7 @@
<d:CountryCode m:type="Int32">1</d:CountryCode>
<d:AreaCode m:type="Int32">425</d:AreaCode>
<d:Number m:type="Int32">9879089</d:Number>
<d:PhoneType>HomePhone</d:PhoneType>
<d:PhoneType m:type="#System.Web.Http.OData.TestCommon.Models.PhoneType">HomePhone</d:PhoneType>
</d:HomeNumber>
<d:FavoriteHobby m:type="#System.Web.Http.OData.TestCommon.Models.IActivity">
<d:ActivityName>Xbox Gaming</d:ActivityName>

Просмотреть файл

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<m:value xmlns:d="http://docs.oasis-open.org/odata/ns/data" xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml" m:context="http://localhost/#System.Web.Http.OData.EnumComplex" m:type="#System.Web.Http.OData.EnumComplex" xmlns:m="http://docs.oasis-open.org/odata/ns/metadata">
<d:RequiredColor>Red, Blue</d:RequiredColor>
<d:RequiredColor m:type="#System.Web.Http.OData.Builder.TestModels.Color">Red, Blue</d:RequiredColor>
<d:NullableColor m:null="true" />
<d:UndefinedColor>123</d:UndefinedColor>
<d:UndefinedColor m:type="#System.Web.Http.OData.Builder.TestModels.Color">123</d:UndefinedColor>
</m:value>

Просмотреть файл

@ -21,7 +21,7 @@
<m:properties>
<d:EmployeeId m:type="Int64">0</d:EmployeeId>
<d:Age m:type="Int32">20</d:Age>
<d:Gender>Male</d:Gender>
<d:Gender m:type="#System.Web.Http.OData.TestCommon.Models.Gender">Male</d:Gender>
<d:FirstName>Frank</d:FirstName>
<d:Alias m:type="#Collection(String)">
<m:element>Alias0</m:element>
@ -36,7 +36,7 @@
<d:CountryCode m:type="Int32">1</d:CountryCode>
<d:AreaCode m:type="Int32">425</d:AreaCode>
<d:Number m:type="Int32">9879089</d:Number>
<d:PhoneType>HomePhone</d:PhoneType>
<d:PhoneType m:type="#System.Web.Http.OData.TestCommon.Models.PhoneType">HomePhone</d:PhoneType>
</d:HomeNumber>
<d:FavoriteHobby m:type="#System.Web.Http.OData.TestCommon.Models.IActivity">
<d:ActivityName>Xbox Gaming</d:ActivityName>
@ -61,7 +61,7 @@
<m:properties>
<d:EmployeeId m:type="Int64">1</d:EmployeeId>
<d:Age m:type="Int32">21</d:Age>
<d:Gender>Male</d:Gender>
<d:Gender m:type="#System.Web.Http.OData.TestCommon.Models.Gender">Male</d:Gender>
<d:FirstName>Steve</d:FirstName>
<d:Alias m:type="#Collection(String)">
<m:element>Alias1</m:element>
@ -76,7 +76,7 @@
<d:CountryCode m:type="Int32">1</d:CountryCode>
<d:AreaCode m:type="Int32">425</d:AreaCode>
<d:Number m:type="Int32">9879090</d:Number>
<d:PhoneType>HomePhone</d:PhoneType>
<d:PhoneType m:type="#System.Web.Http.OData.TestCommon.Models.PhoneType">HomePhone</d:PhoneType>
</d:HomeNumber>
<d:FavoriteHobby m:type="#System.Web.Http.OData.TestCommon.Models.IActivity">
<d:ActivityName>Xbox Gaming</d:ActivityName>

Просмотреть файл

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<m:value xmlns:d="http://docs.oasis-open.org/odata/ns/data" xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml" m:context="http://localhost/#System.Web.Http.OData.TestCommon.Models.Person" m:type="#System.Web.Http.OData.TestCommon.Models.Person" xmlns:m="http://docs.oasis-open.org/odata/ns/metadata">
<d:Age m:type="Int32">20</d:Age>
<d:Gender>Male</d:Gender>
<d:Gender m:type="#System.Web.Http.OData.TestCommon.Models.Gender">Male</d:Gender>
<d:FirstName>Frank</d:FirstName>
<d:Alias m:type="#Collection(String)">
<m:element>Alias0</m:element>
@ -16,7 +16,7 @@
<d:CountryCode m:type="Int32">1</d:CountryCode>
<d:AreaCode m:type="Int32">425</d:AreaCode>
<d:Number m:type="Int32">9879089</d:Number>
<d:PhoneType>HomePhone</d:PhoneType>
<d:PhoneType m:type="#System.Web.Http.OData.TestCommon.Models.PhoneType">HomePhone</d:PhoneType>
</d:HomeNumber>
<d:FavoriteHobby m:type="#System.Web.Http.OData.TestCommon.Models.IActivity">
<d:ActivityName>Xbox Gaming</d:ActivityName>

Просмотреть файл

@ -15,6 +15,7 @@
<d:Age m:type="Int32">10</d:Age>
<d:MyGuid m:type="Guid">f99080c0-2f9e-472e-8c72-1a8ecd9f902d</d:MyGuid>
<d:Name>Asha</d:Name>
<d:FavoriteColor m:type="#System.Web.Http.OData.Builder.TestModels.Color">Red, Green</d:FavoriteColor>
<d:Order m:type="#System.Web.Http.OData.Formatter.FormatterOrder">
<d:OrderAmount m:type="Int32">235342</d:OrderAmount>
<d:OrderName>FirstOrder</d:OrderName>

Просмотреть файл

@ -1,5 +1,5 @@
{
"@odata.context":"http://localhost:8081/$metadata#People/$entity","@odata.id":"http://localhost:8081/People(10)","PerId":10,"Age":10,"MyGuid":"f99080c0-2f9e-472e-8c72-1a8ecd9f902d","Name":"Asha","Order":{
"@odata.context":"http://localhost:8081/$metadata#People/$entity","@odata.id":"http://localhost:8081/People(10)","PerId":10,"Age":10,"MyGuid":"f99080c0-2f9e-472e-8c72-1a8ecd9f902d","Name":"Asha","FavoriteColor":"Red, Green","Order":{
"OrderAmount":235342,"OrderName":"FirstOrder"
}
}

Просмотреть файл

@ -1,5 +1,5 @@
{
"@odata.context":"http://localhost:8081/$metadata#People/$entity","@odata.type":"#System.Web.Http.OData.Formatter.FormatterPerson","@odata.id":"http://localhost:8081/People(10)","PerId":10,"Age":10,"MyGuid@odata.type":"Edm.Guid","MyGuid":"f99080c0-2f9e-472e-8c72-1a8ecd9f902d","Name":"Asha","Order":{
"@odata.context":"http://localhost:8081/$metadata#People/$entity","@odata.type":"#System.Web.Http.OData.Formatter.FormatterPerson","@odata.id":"http://localhost:8081/People(10)","PerId":10,"Age":10,"MyGuid@odata.type":"Edm.Guid","MyGuid":"f99080c0-2f9e-472e-8c72-1a8ecd9f902d","Name":"Asha","FavoriteColor@odata.type":"#System.Web.Http.OData.Builder.TestModels.Color","FavoriteColor":"Red, Green","Order":{
"@odata.type":"#System.Web.Http.OData.Formatter.FormatterOrder","OrderAmount":235342,"OrderName":"FirstOrder"
}
}

Просмотреть файл

@ -1,5 +1,5 @@
{
"@odata.context":"http://localhost:8081/$metadata#People/$entity","@odata.id":"http://localhost:8081/People(10)","PerId":10,"Age":10,"MyGuid":"f99080c0-2f9e-472e-8c72-1a8ecd9f902d","Name":"Asha","Order":{
"@odata.context":"http://localhost:8081/$metadata#People/$entity","@odata.id":"http://localhost:8081/People(10)","PerId":10,"Age":10,"MyGuid":"f99080c0-2f9e-472e-8c72-1a8ecd9f902d","Name":"Asha","FavoriteColor":"Red, Green","Order":{
"OrderAmount":235342,"OrderName":"FirstOrder"
}
}

Просмотреть файл

@ -1,5 +1,5 @@
{
"PerId":10,"Age":10,"MyGuid":"f99080c0-2f9e-472e-8c72-1a8ecd9f902d","Name":"Asha","Order":{
"PerId":10,"Age":10,"MyGuid":"f99080c0-2f9e-472e-8c72-1a8ecd9f902d","Name":"Asha","FavoriteColor":"Red, Green","Order":{
"OrderAmount":235342,"OrderName":"FirstOrder"
}
}

Просмотреть файл

@ -1 +1 @@
{"Age":10,"MyGuid":"f99080c0-2f9e-472e-8c72-1a8ecd9f902d","Name":"Asha","Order":{"OrderAmount":235342,"OrderName":"FirstOrder"},"PerId":10}
{"Age":10,"MyGuid":"f99080c0-2f9e-472e-8c72-1a8ecd9f902d","Name":"Asha","FavoriteColor":3,"Order":{"OrderAmount":235342,"OrderName":"FirstOrder"},"PerId":10}

Просмотреть файл

@ -115,6 +115,7 @@
<Compile Include="OData\Formatter\ODataRawValueMediaTypeMappingTests.cs" />
<Compile Include="OData\Formatter\ODataValueExtensionsTest.cs" />
<Compile Include="OData\Formatter\Serialization\ODataEntityReferenceLinksSerializerTest.cs" />
<Compile Include="OData\Formatter\Serialization\ODataEnumTypeSerializerTests.cs" />
<Compile Include="OData\Formatter\Serialization\ODataRawValueSerializerTests.cs" />
<Compile Include="OData\Formatter\Serialization\ODataSerializerContextTest.cs" />
<Compile Include="OData\Formatter\Serialization\ODataWorkspaceSerializerTest.cs" />
@ -354,7 +355,7 @@
<EmbeddedResource Include="Resources\DecimalInJsonFullMetadata.json" />
<EmbeddedResource Include="Resources\DoubleInJsonFullMetadata.json" />
<EmbeddedResource Include="Resources\EmployeeEntryInJsonLight.json" />
<EmbeddedResource Include="Resources\EnumComplexTypeInJsonLight.json" />
<EmbeddedResource Include="Resources\EnumComplexTypeInJsonLight.json" />
<EmbeddedResource Include="Resources\FeedOfEmployeeInJsonLight.json" />
<EmbeddedResource Include="Resources\GuidInJsonFullMetadata.json" />
<EmbeddedResource Include="Resources\Int16InJsonFullMetadata.json" />

Просмотреть файл

@ -51,6 +51,33 @@ namespace System.Web.Http.OData
return builder;
}
public static ODataModelBuilder Add_ByteEnum_EnumType(this ODataModelBuilder builder)
{
EnumTypeConfiguration<ByteEnum> byteEnum = builder.EnumType<ByteEnum>();
byteEnum.Member(ByteEnum.FirstByte);
byteEnum.Member(ByteEnum.SecondByte);
byteEnum.Member(ByteEnum.ThirdByte);
return builder;
}
public static ODataModelBuilder Add_SByteEnum_EnumType(this ODataModelBuilder builder)
{
EnumTypeConfiguration<SByteEnum> sByteEnum = builder.EnumType<SByteEnum>();
sByteEnum.Member(SByteEnum.FirstSByte);
sByteEnum.Member(SByteEnum.SecondSByte);
sByteEnum.Member(SByteEnum.ThirdSByte);
return builder;
}
public static ODataModelBuilder Add_ShortEnum_EnumType(this ODataModelBuilder builder)
{
EnumTypeConfiguration<ShortEnum> shortEnum = builder.EnumType<ShortEnum>();
shortEnum.Member(ShortEnum.FirstShort);
shortEnum.Member(ShortEnum.SecondShort);
shortEnum.Member(ShortEnum.ThirdShort);
return builder;
}
public static ODataModelBuilder Add_Address_ComplexType(this ODataModelBuilder builder)
{
var address = builder.ComplexType<Address>();