diff options
author | raghuramn <ranadimi@microsoft.com> | 2012-10-06 04:08:34 +0400 |
---|---|---|
committer | raghuramn <ranadimi@microsoft.com> | 2012-10-10 04:12:48 +0400 |
commit | 41b15c411ddab94876d270868e0bc3d9faa4f59a (patch) | |
tree | 322aba1286f6fe00b897dd2c9af0feb8bc35a58a | |
parent | 1b5b1c247f12889f4bd9238723d80ad02ac457a1 (diff) |
Issue 484: Self link generation might fail if the model has non-standard
edm primitives
we are not converting non-standard primitives to their standard
representation before passing on to the ODataUriParser for generating uri
representation.
4 files changed, 122 insertions, 4 deletions
diff --git a/src/System.Web.Http.OData/OData/Builder/Conventions/ConventionsHelpers.cs b/src/System.Web.Http.OData/OData/Builder/Conventions/ConventionsHelpers.cs index 5c29406e..b1661907 100644 --- a/src/System.Web.Http.OData/OData/Builder/Conventions/ConventionsHelpers.cs +++ b/src/System.Web.Http.OData/OData/Builder/Conventions/ConventionsHelpers.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.Linq; using System.Reflection; using System.Web.Http.OData.Formatter; +using System.Web.Http.OData.Formatter.Serialization; using System.Web.Http.OData.Properties; using Microsoft.Data.OData.Query; @@ -48,7 +49,7 @@ namespace System.Web.Http.OData.Builder.Conventions // TODO: BUG 453795: reflection cleanup if (entityTypeConfiguration.Keys.Count() == 1) { - return GetUriRepresentationForKeyValue(entityTypeConfiguration.Keys.First().PropertyInfo, entityContext.EntityInstance); + return GetUriRepresentationForKeyValue(entityTypeConfiguration.Keys.First().PropertyInfo, entityContext.EntityInstance, entityTypeConfiguration); } else { @@ -57,7 +58,7 @@ namespace System.Web.Http.OData.Builder.Conventions entityTypeConfiguration .Keys .Select( - key => String.Format(CultureInfo.InvariantCulture, "{0}={1}", key.Name, GetUriRepresentationForKeyValue(key.PropertyInfo, entityContext.EntityInstance)))); + key => String.Format(CultureInfo.InvariantCulture, "{0}={1}", key.Name, GetUriRepresentationForKeyValue(key.PropertyInfo, entityContext.EntityInstance, entityTypeConfiguration)))); } } @@ -159,12 +160,30 @@ namespace System.Web.Http.OData.Builder.Conventions return false; } - private static string GetUriRepresentationForKeyValue(PropertyInfo key, object entityInstance) + // gets the primitive odata uri representation. + public static string GetUriRepresentationForValue(object value) + { + Contract.Assert(value != null); + Contract.Assert(EdmLibHelpers.GetEdmPrimitiveTypeOrNull(value.GetType()) != null); + + value = ODataPrimitiveSerializer.ConvertUnsupportedPrimitives(value); + return ODataUriBuilder.GetUriRepresentation(value); + } + + private static string GetUriRepresentationForKeyValue(PropertyInfo key, object entityInstance, IEntityTypeConfiguration entityType) { Contract.Assert(key != null); Contract.Assert(entityInstance != null); + Contract.Assert(entityType != null); + + object value = key.GetValue(entityInstance, null); + + if (value == null) + { + throw Error.InvalidOperation(SRResources.KeyValueCannotBeNull, key.Name, entityType.FullName); + } - return ODataUriBuilder.GetUriRepresentation(key.GetValue(entityInstance, null)); + return GetUriRepresentationForValue(value); } private class PropertyEqualityComparer : IEqualityComparer<PropertyInfo> diff --git a/src/System.Web.Http.OData/Properties/SRResources.Designer.cs b/src/System.Web.Http.OData/Properties/SRResources.Designer.cs index d4e79465..7537b7e3 100644 --- a/src/System.Web.Http.OData/Properties/SRResources.Designer.cs +++ b/src/System.Web.Http.OData/Properties/SRResources.Designer.cs @@ -502,6 +502,15 @@ namespace System.Web.Http.OData.Properties { } /// <summary> + /// Looks up a localized string similar to Key property '{0}' of type '{1}' is null. Key properties cannot have null values.. + /// </summary> + internal static string KeyValueCannotBeNull { + get { + return ResourceManager.GetString("KeyValueCannotBeNull", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to The LambdaExpression must have exactly one parameter.. /// </summary> internal static string LambdaExpressionMustHaveExactlyOneParameter { diff --git a/src/System.Web.Http.OData/Properties/SRResources.resx b/src/System.Web.Http.OData/Properties/SRResources.resx index 98d1bfbd..b5c15cb0 100644 --- a/src/System.Web.Http.OData/Properties/SRResources.resx +++ b/src/System.Web.Http.OData/Properties/SRResources.resx @@ -438,4 +438,7 @@ <data name="TargetEntityTypeMissing" xml:space="preserve"> <value>Could not find the target entity type for the navigation property '{0}' on entity type '{1}'.</value> </data> + <data name="KeyValueCannotBeNull" xml:space="preserve"> + <value>Key property '{0}' of type '{1}' is null. Key properties cannot have null values.</value> + </data> </root>
\ No newline at end of file diff --git a/test/System.Web.Http.OData.Test/OData/Builder/Conventions/ConventionsHelpersTests.cs b/test/System.Web.Http.OData.Test/OData/Builder/Conventions/ConventionsHelpersTests.cs index 5cfead43..992c1f06 100644 --- a/test/System.Web.Http.OData.Test/OData/Builder/Conventions/ConventionsHelpersTests.cs +++ b/test/System.Web.Http.OData.Test/OData/Builder/Conventions/ConventionsHelpersTests.cs @@ -4,6 +4,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; using Microsoft.TestCommon; +using Microsoft.TestCommon.Types; using Moq; namespace System.Web.Http.OData.Builder.Conventions @@ -27,6 +28,42 @@ namespace System.Web.Http.OData.Builder.Conventions } } + public static TheoryDataSet<object, string> GetUriRepresentationForValue_DataSet + { + get + { + return new TheoryDataSet<object, string>() + { + { (bool)true, "true" }, + { (char)'1', "'1'" }, + { (char?)'1', "'1'" }, + { (string)"123", "'123'" }, + { (char[])new char[] { '1', '2', '3' }, "'123'" }, + { (int)123, "123" }, + { (short)123, "123" }, + { (long)123, "123L" }, + { (ushort)123, "123" }, + { (uint)123, "123L" }, + { (ulong)123, "123L" }, + { (int?)123, "123" }, + { (short?)123, "123" }, + { (long?)123, "123L" }, + { (ushort?)123, "123" }, + { (uint?)123, "123L" }, + { (ulong?)123, "123L" }, + { (float)123.123, "123.12f" }, + { (double)123.123, "123.123" }, + { (decimal)123.123, "123.123M" }, + { Guid.Empty, "guid'00000000-0000-0000-0000-000000000000'" }, + { DateTime.FromBinary(0), "datetime'0001-01-01T00:00:00'" }, + { TimeSpan.FromSeconds(86456), "time'P1DT56S'" }, + { DateTimeOffset.FromFileTime(0), "datetimeoffset'1600-12-31T16:00:00-08:00'" }, + { SimpleEnum.First, "'First'" }, + { FlagsEnum.One | FlagsEnum.Two, "'One%2C%20Two'" }, + }; + } + } + [Theory] [InlineData(typeof(ICollection<string>), typeof(string))] [InlineData(typeof(IList<string>), typeof(string))] @@ -162,6 +199,56 @@ namespace System.Web.Http.OData.Builder.Conventions Assert.Equal("Key1='key1',Key2=2,Key3=true", keyValue); } + [Fact] + public void GetEntityKeyValue_ThrowsForNullKeys() + { + // Arrange + IStructuralTypeConfiguration structuralType = new Mock<IStructuralTypeConfiguration>().Object; + var entityInstance = new { Key = (string)null }; + PrimitivePropertyConfiguration[] keys = { new PrimitivePropertyConfiguration(entityInstance.GetType().GetProperty("Key"), structuralType) }; + + Mock<IEntityTypeConfiguration> entityType = new Mock<IEntityTypeConfiguration>(); + entityType.Setup(e => e.Keys).Returns(keys); + entityType.Setup(e => e.FullName).Returns("FullName"); + + // Act & Assert + Assert.Throws<InvalidOperationException>( + () => ConventionsHelpers.GetEntityKeyValue(new EntityInstanceContext { EntityInstance = entityInstance }, entityType.Object), + "Key property 'Key' of type 'FullName' is null. Key properties cannot have null values."); + } + + [Fact] + public void GetEntityKeyValue_ThrowsForNullKeys_WithMultipleKeys() + { + // Arrange + IStructuralTypeConfiguration structuralType = new Mock<IStructuralTypeConfiguration>().Object; + var entityInstance = new { Key1 = "abc", Key2 = "def", Key3 = (string)null }; + PrimitivePropertyConfiguration[] keys = + { + new PrimitivePropertyConfiguration(entityInstance.GetType().GetProperty("Key1"), structuralType), + new PrimitivePropertyConfiguration(entityInstance.GetType().GetProperty("Key2"), structuralType), + new PrimitivePropertyConfiguration(entityInstance.GetType().GetProperty("Key3"), structuralType), + }; + + Mock<IEntityTypeConfiguration> entityType = new Mock<IEntityTypeConfiguration>(); + entityType.Setup(e => e.Keys).Returns(keys); + entityType.Setup(e => e.FullName).Returns("EntityType"); + + // Act & Assert + Assert.Throws<InvalidOperationException>( + () => ConventionsHelpers.GetEntityKeyValue(new EntityInstanceContext { EntityInstance = entityInstance }, entityType.Object), + "Key property 'Key3' of type 'EntityType' is null. Key properties cannot have null values."); + } + + [Theory] + [PropertyData("GetUriRepresentationForValue_DataSet")] + public void GetUriRepresentationForValue_Works(object value, string result) + { + Assert.Equal( + result, + ConventionsHelpers.GetUriRepresentationForValue(value)); + } + private sealed class IsCollection_with_Collections_TestClass : List<bool> { } |