From 12d998c0e60ed9bcf1a45dbdc650a51a3578b763 Mon Sep 17 00:00:00 2001 From: Sam Xu Date: Thu, 2 Aug 2018 15:23:07 -0700 Subject: [PATCH] Resolve the comments and add more unit tests --- .../Builder/EdmModelHelperMethods.cs | 23 ++++++++---- .../ClrEnumMemberAnnotation.cs | 28 +++++--------- .../Deserialization/ODataEnumDeserializer.cs | 5 +-- .../Formatter/EdmLibHelpers.cs | 4 +- .../Serialization/ODataEnumSerializer.cs | 2 +- .../Builder/EnumTypeTest.cs | 21 +++++++++-- .../ODataEnumDeserializerTests.cs | 36 ++++++++++++++++++ .../ODataEnumTypeSerializerTests.cs | 37 +++++++++++++++++++ 8 files changed, 122 insertions(+), 34 deletions(-) diff --git a/src/Microsoft.AspNet.OData.Shared/Builder/EdmModelHelperMethods.cs b/src/Microsoft.AspNet.OData.Shared/Builder/EdmModelHelperMethods.cs index 134c9d66e5..0339a1c4d1 100644 --- a/src/Microsoft.AspNet.OData.Shared/Builder/EdmModelHelperMethods.cs +++ b/src/Microsoft.AspNet.OData.Shared/Builder/EdmModelHelperMethods.cs @@ -440,13 +440,7 @@ private static Dictionary AddTypes(this EdmModel model, EdmTypeM // add annotation for properties Dictionary edmProperties = edmTypeMap.EdmProperties; model.AddClrPropertyInfoAnnotations(edmProperties); - - // add Enum mapping annotation - if (edmTypeMap.EnumMembers != null && edmTypeMap.EnumMembers.Any()) - { - model.SetAnnotationValue(model, new ClrEnumMemberAnnotation(edmTypeMap.EnumMembers)); - } - + model.AddClrEnumMemberInfoAnnotations(edmTypeMap); model.AddPropertyRestrictionsAnnotations(edmTypeMap.EdmPropertiesRestrictions); model.AddPropertiesQuerySettings(edmTypeMap.EdmPropertiesQuerySettings); model.AddStructuredTypeQuerySettings(edmTypeMap.EdmStructuredTypeQuerySettings); @@ -522,6 +516,21 @@ private static void AddClrPropertyInfoAnnotations(this EdmModel model, Dictionar } } + private static void AddClrEnumMemberInfoAnnotations(this EdmModel model, EdmTypeMap edmTypeMap) + { + if (edmTypeMap.EnumMembers == null || !edmTypeMap.EnumMembers.Any()) + { + return; + } + + var enumGroupBy = edmTypeMap.EnumMembers.GroupBy(e => e.Key.GetType(), e => e); + foreach (var enumGroup in enumGroupBy) + { + IEdmType edmType = edmTypeMap.EdmTypes[enumGroup.Key]; + model.SetAnnotationValue(edmType, new ClrEnumMemberAnnotation(enumGroup.ToDictionary(e => e.Key, e => e.Value))); + } + } + private static void AddDynamicPropertyDictionaryAnnotations(this EdmModel model, Dictionary openTypes) { diff --git a/src/Microsoft.AspNet.OData.Shared/ClrEnumMemberAnnotation.cs b/src/Microsoft.AspNet.OData.Shared/ClrEnumMemberAnnotation.cs index a2f602f635..c5d7454463 100644 --- a/src/Microsoft.AspNet.OData.Shared/ClrEnumMemberAnnotation.cs +++ b/src/Microsoft.AspNet.OData.Shared/ClrEnumMemberAnnotation.cs @@ -9,12 +9,12 @@ namespace Microsoft.AspNet.OData { /// - /// Represents a mapping from an to a CLR Enum member. + /// Represents a mapping betwwen an and a CLR Enum member. /// public class ClrEnumMemberAnnotation { - private IDictionary _maps; - private IDictionary _reverseMaps; + private IDictionary _map; + private IDictionary _reverseMap; /// /// Initializes a new instance of class. @@ -27,11 +27,11 @@ public ClrEnumMemberAnnotation(IDictionary map) throw Error.ArgumentNull("map"); } - _maps = map; - _reverseMaps = new Dictionary(); + _map = map; + _reverseMap = new Dictionary(); foreach (var item in map) { - _reverseMaps.Add(item.Value, item.Key); + _reverseMap.Add(item.Value, item.Key); } } @@ -43,12 +43,8 @@ public ClrEnumMemberAnnotation(IDictionary map) public IEdmEnumMember GetEdmEnumMember(Enum clrEnumMemberInfo) { IEdmEnumMember value; - if (_maps.TryGetValue(clrEnumMemberInfo, out value)) - { - return value; - } - - return null; + _map.TryGetValue(clrEnumMemberInfo, out value); + return value; } /// @@ -59,12 +55,8 @@ public IEdmEnumMember GetEdmEnumMember(Enum clrEnumMemberInfo) public Enum GetClrEnumMember(IEdmEnumMember edmEnumMember) { Enum value; - if (_reverseMaps.TryGetValue(edmEnumMember, out value)) - { - return value; - } - - return null; + _reverseMap.TryGetValue(edmEnumMember, out value); + return value; } } } diff --git a/src/Microsoft.AspNet.OData.Shared/Formatter/Deserialization/ODataEnumDeserializer.cs b/src/Microsoft.AspNet.OData.Shared/Formatter/Deserialization/ODataEnumDeserializer.cs index cb9b49ec87..400f319ad5 100644 --- a/src/Microsoft.AspNet.OData.Shared/Formatter/Deserialization/ODataEnumDeserializer.cs +++ b/src/Microsoft.AspNet.OData.Shared/Formatter/Deserialization/ODataEnumDeserializer.cs @@ -61,6 +61,7 @@ public override object ReadInline(object item, IEdmTypeReference edmType, ODataD } IEdmEnumTypeReference enumTypeReference = (IEdmEnumTypeReference)edmType; + IEdmEnumType enumType = enumTypeReference.EnumDefinition(); ODataEnumValue enumValue = item as ODataEnumValue; if (readContext.IsUntyped) { @@ -69,11 +70,9 @@ public override object ReadInline(object item, IEdmTypeReference edmType, ODataD } // Enum member supports model alias case. So, try to use the Edm member name to retrieve the Enum value. - var memberMapAnnotation = readContext.Model.GetClrEnumMemberAnnotation(); + var memberMapAnnotation = readContext.Model.GetClrEnumMemberAnnotation(enumType); if (memberMapAnnotation != null) { - IEdmEnumType enumType = enumTypeReference.EnumDefinition(); - if (enumValue != null) { IEdmEnumMember enumMember = enumType.Members.FirstOrDefault(m => m.Name == enumValue.Value); diff --git a/src/Microsoft.AspNet.OData.Shared/Formatter/EdmLibHelpers.cs b/src/Microsoft.AspNet.OData.Shared/Formatter/EdmLibHelpers.cs index bdbd4aba6a..f00f98de28 100644 --- a/src/Microsoft.AspNet.OData.Shared/Formatter/EdmLibHelpers.cs +++ b/src/Microsoft.AspNet.OData.Shared/Formatter/EdmLibHelpers.cs @@ -704,14 +704,14 @@ public static string GetClrPropertyName(IEdmProperty edmProperty, IEdmModel edmM return propertyName; } - public static ClrEnumMemberAnnotation GetClrEnumMemberAnnotation(this IEdmModel edmModel) + public static ClrEnumMemberAnnotation GetClrEnumMemberAnnotation(this IEdmModel edmModel, IEdmEnumType enumType) { if (edmModel == null) { throw Error.ArgumentNull("edmModel"); } - ClrEnumMemberAnnotation annotation = edmModel.GetAnnotationValue(edmModel); + ClrEnumMemberAnnotation annotation = edmModel.GetAnnotationValue(enumType); if (annotation != null) { return annotation; diff --git a/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataEnumSerializer.cs b/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataEnumSerializer.cs index 4a3f8962a9..eb8f83927c 100644 --- a/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataEnumSerializer.cs +++ b/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataEnumSerializer.cs @@ -91,7 +91,7 @@ public virtual ODataEnumValue CreateODataEnumValue(object graph, IEdmEnumTypeRef } // Enum member supports model alias case. So, try to use the Edm member name to create Enum value. - var memberMapAnnotation = writeContext.Model.GetClrEnumMemberAnnotation(); + var memberMapAnnotation = writeContext.Model.GetClrEnumMemberAnnotation(enumType.EnumDefinition()); if (memberMapAnnotation != null) { var edmEnumMember = memberMapAnnotation.GetEdmEnumMember((Enum)graph); diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/EnumTypeTest.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/EnumTypeTest.cs index fae9e0a3be..b05b805a6f 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/EnumTypeTest.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/EnumTypeTest.cs @@ -1084,12 +1084,20 @@ public void ODataConventionModelBuilder_DataContractAttribute_WorksOnEnumType() // Assert IEdmEnumType enumType = model.SchemaElements.OfType().Single(); Assert.NotNull(enumType); - Assert.Equal(3, enumType.Members.Count()); + Assert.Equal(4, enumType.Members.Count()); Assert.Equal("Feelings", enumType.Name); Assert.Equal("Test", enumType.Namespace); Assert.Contains(enumType.Members, (m) => m.Name.Equals("happy")); Assert.Contains(enumType.Members, (m) => m.Name.Equals("sad")); Assert.Contains(enumType.Members, (m) => m.Name.Equals("KeepDefaultName")); + Assert.Contains(enumType.Members, (m) => m.Name.Equals("soso")); + + var annotation = model.GetClrEnumMemberAnnotation(enumType); + Assert.NotNull(annotation); + + IEdmEnumMember enumMember = enumType.Members.Single(m => m.Name.Equals("soso")); + Assert.Same(enumMember, annotation.GetEdmEnumMember(Life.NotTooBad)); + Assert.Equal(Life.NotTooBad, annotation.GetClrEnumMember(enumMember)); } [Fact] @@ -1118,13 +1126,14 @@ public void ODataConventionModelBuilder_DataContractAttribute_WithAddedExplicitl // Assert IEdmEnumType enumType = model.SchemaElements.OfType().Single(); Assert.NotNull(enumType); - Assert.Equal(4, enumType.Members.Count()); + Assert.Equal(5, enumType.Members.Count()); Assert.Equal("Feelings", enumType.Name); Assert.Equal("Test", enumType.Namespace); Assert.Contains(enumType.Members, (m) => m.Name.Equals("happy")); Assert.Contains(enumType.Members, (m) => m.Name.Equals("sad")); Assert.Contains(enumType.Members, (m) => m.Name.Equals("JustSoSo")); Assert.Contains(enumType.Members, (m) => m.Name.Equals("KeepDefaultName")); + Assert.Contains(enumType.Members, (m) => m.Name.Equals("soso")); } private IEdmStructuredType AddComplexTypeWithODataConventionModelBuilder() @@ -1192,10 +1201,16 @@ public enum Life { [EnumMember(Value = "happy")] Happy = 1, + [EnumMember(Value = "sad")] Sad = 2, + JustSoSo = 3, + [EnumMember] - KeepDefaultName + KeepDefaultName = 4, + + [EnumMember(Value = "soso")] + NotTooBad } } \ No newline at end of file diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Formatter/Deserialization/ODataEnumDeserializerTests.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Formatter/Deserialization/ODataEnumDeserializerTests.cs index 9066a5d937..22a62f80bc 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Formatter/Deserialization/ODataEnumDeserializerTests.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Formatter/Deserialization/ODataEnumDeserializerTests.cs @@ -3,6 +3,7 @@ using System.Net.Http; using System.Net.Http.Headers; +using System.Runtime.Serialization; using Microsoft.AspNet.OData.Formatter.Deserialization; using Microsoft.AspNet.OData.Test.Abstraction; using Microsoft.OData; @@ -68,6 +69,32 @@ public void ReadFromStreamAsync_ForUnType() Assert.Equal("Blue", color.Value); } + [Fact] + public void ReadFromStreamAsync_ModelAlias() + { + // Arrange + string content = "{\"@odata.type\":\"#NS.level\",\"value\":\"veryhigh\"}"; + + var builder = ODataConventionModelBuilderFactory.Create(); + builder.EnumType().Namespace = "NS"; + IEdmModel model = builder.GetEdmModel(); + + ODataEnumDeserializer deserializer = new ODataEnumDeserializer(); + ODataDeserializerContext readContext = new ODataDeserializerContext + { + Model = model, + ResourceType = typeof(Level) + }; + + // Act + object value = deserializer.Read(GetODataMessageReader(GetODataMessage(content), model), + typeof(Level), readContext); + + // Assert + Level level = Assert.IsType(value); + Assert.Equal(Level.High, level); + } + private static ODataMessageReader GetODataMessageReader(IODataRequestMessage oDataRequestMessage, IEdmModel edmModel) { return new ODataMessageReader(oDataRequestMessage, new ODataMessageReaderSettings(), edmModel); @@ -97,5 +124,14 @@ public enum Color Blue, Green } + + [DataContract(Name = "level")] + public enum Level + { + [EnumMember(Value = "low")] + Low, + [EnumMember(Value = "veryhigh")] + High + } } } diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Formatter/Serialization/ODataEnumTypeSerializerTests.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Formatter/Serialization/ODataEnumTypeSerializerTests.cs index 98cb221c00..3f76aefae6 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Formatter/Serialization/ODataEnumTypeSerializerTests.cs +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Formatter/Serialization/ODataEnumTypeSerializerTests.cs @@ -3,8 +3,11 @@ using Microsoft.AspNet.OData.Formatter; using Microsoft.AspNet.OData.Formatter.Serialization; +using Microsoft.AspNet.OData.Test.Abstraction; using Microsoft.OData; using Microsoft.OData.Edm; +using System.Linq; +using System.Runtime.Serialization; using Xunit; namespace Microsoft.AspNet.OData.Test.Formatter.Serialization @@ -60,5 +63,39 @@ public void AddTypeNameAnnotationAsNeeded_AddsNullAnnotation_InNoMetadataMode() Assert.NotNull(annotation); Assert.Null(annotation.TypeName); } + + [Fact] + public void CreateODataEnumValue_ReturnsCorrectEnumMember() + { + // Arrange + var builder = ODataConventionModelBuilderFactory.Create(); + builder.EnumType().Namespace = "NS"; + IEdmModel model = builder.GetEdmModel(); + IEdmEnumType enumType = model.SchemaElements.OfType().Single(); + + var provider = new DefaultODataSerializerProvider(new MockContainer()); + ODataEnumSerializer serializer = new ODataEnumSerializer(provider); + ODataSerializerContext writeContext = new ODataSerializerContext + { + Model = model + }; + + // Act + ODataEnumValue value = serializer.CreateODataEnumValue(BookCategory.Newspaper, + new EdmEnumTypeReference(enumType, false), writeContext); + + // Assert + Assert.NotNull(value); + Assert.Equal("news", value.Value); + } + } + + [DataContract(Name = "category")] + public enum BookCategory + { + [EnumMember(Value = "cartoon")] + Cartoon, + [EnumMember(Value = "news")] + Newspaper } }