diff --git a/src/Microsoft.OData.Client/Metadata/ClientTypeUtil.cs b/src/Microsoft.OData.Client/Metadata/ClientTypeUtil.cs
index 13b6696fd5..768b650d9c 100644
--- a/src/Microsoft.OData.Client/Metadata/ClientTypeUtil.cs
+++ b/src/Microsoft.OData.Client/Metadata/ClientTypeUtil.cs
@@ -1,4 +1,4 @@
-//---------------------------------------------------------------------
+//---------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
//
@@ -18,6 +18,7 @@ namespace Microsoft.OData.Client.Metadata
using Microsoft.OData.Metadata;
using Microsoft.OData.Edm;
using Client = Microsoft.OData.Client;
+ using System.Runtime.CompilerServices;
#endregion Namespaces.
@@ -835,5 +836,20 @@ private static bool SkipAssembly(Assembly assembly)
|| assembly.Equals(typeof(EdmModel).Assembly) // OData Edm assembly
|| assembly.Equals(typeof(Spatial.Geography).Assembly); // Spatial assembly
}
+
+
+ internal static bool IsAnonymousProperty(this PropertyInfo propertyInfo)
+ {
+ return propertyInfo.DeclaringType.IsAnonymousType();
+ }
+
+ internal static bool IsAnonymousType(this Type type)
+ {
+ return type.IsDefined(typeof(CompilerGeneratedAttribute), false)
+ && type.IsGenericType
+ && (type.Name.Contains("AnonymousType", StringComparison.Ordinal) || type.Name.Contains("AnonType", StringComparison.Ordinal))
+ && (type.Name.StartsWith("<>") || type.Name.StartsWith("VB$"))
+ && (type.Attributes & TypeAttributes.NotPublic) == TypeAttributes.NotPublic;
+ }
}
}
diff --git a/src/Microsoft.OData.Client/Metadata/ODataTypeInfo.cs b/src/Microsoft.OData.Client/Metadata/ODataTypeInfo.cs
index 0a8a47fc11..9f6ef0ea87 100644
--- a/src/Microsoft.OData.Client/Metadata/ODataTypeInfo.cs
+++ b/src/Microsoft.OData.Client/Metadata/ODataTypeInfo.cs
@@ -1,4 +1,4 @@
-//---------------------------------------------------------------------
+//---------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
//
@@ -46,7 +46,7 @@ internal class ODataTypeInfo
public ODataTypeInfo(Type type)
{
this.type = type;
- ServerSideNameDict = new ConcurrentDictionary();
+ ServerSideNameDict = new ConcurrentDictionary();
}
///
@@ -84,8 +84,8 @@ public bool HasProperties
///
/// Sertver defined type name
///
- public string ServerDefinedTypeName
- {
+ public string ServerDefinedTypeName
+ {
get
{
if (_serverDefinedTypeName == null)
@@ -102,7 +102,7 @@ public string ServerDefinedTypeName
else
{
_serverDefinedTypeName = type.Name;
- }
+ }
}
return _serverDefinedTypeName;
@@ -251,9 +251,9 @@ private IEnumerable GetAllProperties()
(typeof(UIntPtr) == propertyType))
{
continue;
- }
+ }
- Debug.Assert(!propertyType.ContainsGenericParameters(), "remove when test case is found that encounters this");
+ Debug.Assert(!propertyType.ContainsGenericParameters(), "remove when test case is found that encounters this");
if (propertyInfo.CanRead &&
(!propertyType.IsValueType() || propertyInfo.CanWrite) &&
@@ -334,10 +334,10 @@ private PropertyInfo[] GetKeyProperties()
if (newKeyKind == KeyKind.AttributedKey && keyProperties.Count != dataServiceKeyAttribute?.KeyNames.Count)
{
var m = (from string a in dataServiceKeyAttribute.KeyNames
- where (from b in Properties
+ where (from b in Properties
where b.Name == a
select b).FirstOrDefault() == null
- select a).First();
+ select a).First();
throw Client.Error.InvalidOperation(Client.Strings.ClientType_MissingProperty(typeName, m));
}
}
@@ -381,10 +381,16 @@ private static void InserKeyBasedOnOrder(List> k
private static KeyKind IsKeyProperty(PropertyInfo propertyInfo, KeyAttribute dataServiceKeyAttribute, out int order)
{
Debug.Assert(propertyInfo != null, "propertyInfo != null");
+ order = -1;
+
+ //If the property's declaring type is anonymous, it is not a key.
+ if (propertyInfo.IsAnonymousProperty())
+ {
+ return KeyKind.NotKey;
+ }
string propertyName = ClientTypeUtil.GetServerDefinedName(propertyInfo);
- order = -1;
KeyKind keyKind = KeyKind.NotKey;
if (dataServiceKeyAttribute != null && dataServiceKeyAttribute.KeyNames.Contains(propertyName))
{
@@ -396,10 +402,10 @@ private static KeyKind IsKeyProperty(PropertyInfo propertyInfo, KeyAttribute dat
order = newOrder;
keyKind = newKind;
}
- else if (propertyName.EndsWith("ID", StringComparison.Ordinal))
+ else if (propertyName.Equals(propertyInfo.DeclaringType.Name + "Id", StringComparison.OrdinalIgnoreCase) || propertyName.Equals("Id", StringComparison.OrdinalIgnoreCase))
{
string declaringTypeName = propertyInfo.DeclaringType.Name;
- if ((propertyName.Length == (declaringTypeName.Length + 2)) && propertyName.StartsWith(declaringTypeName, StringComparison.Ordinal))
+ if ((propertyName.Length == (declaringTypeName.Length + 2)) && propertyName.StartsWith(declaringTypeName, StringComparison.OrdinalIgnoreCase))
{
// matched "DeclaringType.Name+ID" pattern
keyKind = KeyKind.TypeNameId;
@@ -419,7 +425,7 @@ private static bool IsDataAnnotationsKeyProperty(PropertyInfo propertyInfo, out
order = -1;
kind = KeyKind.NotKey;
var attributes = propertyInfo.GetCustomAttributes();
- if(!attributes.Any(a => a is System.ComponentModel.DataAnnotations.KeyAttribute))
+ if (!attributes.Any(a => a is System.ComponentModel.DataAnnotations.KeyAttribute))
{
return false;
}
diff --git a/test/FunctionalTests/Microsoft.OData.Client.Tests/Metadata/ClientTypeUtilTests.cs b/test/FunctionalTests/Microsoft.OData.Client.Tests/Metadata/ClientTypeUtilTests.cs
index db997409d5..f3b97e282d 100644
--- a/test/FunctionalTests/Microsoft.OData.Client.Tests/Metadata/ClientTypeUtilTests.cs
+++ b/test/FunctionalTests/Microsoft.OData.Client.Tests/Metadata/ClientTypeUtilTests.cs
@@ -1,4 +1,4 @@
-//---------------------------------------------------------------------
+//---------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
//
@@ -17,6 +17,19 @@ namespace Microsoft.OData.Client.Tests.Metadata
///
public class ClientTypeUtilTests
{
+ [Theory]
+ [InlineData(typeof(Giraffe), true)]
+ [InlineData(typeof(Hippo), true)]
+ [InlineData(typeof(Ferret), true)]
+ [InlineData(typeof(Lion), false)]
+ public void IfTypeProperty_HasConventionalKey_TypeIsEntity(Type entityType, bool isEntity)
+ {
+ //Act
+ bool actualResult = ClientTypeUtil.TypeOrElementTypeIsEntity(entityType);
+ //Assert
+ Assert.Equal(actualResult, isEntity);
+ }
+
[Fact]
public void IFTypeProperty_HasKeyAttribute_TypeIsEntity()
{
@@ -214,5 +227,37 @@ public struct EmployeeTypeStruct
public int EmpTypeId { get; set; }
}
+ public class Giraffe
+ {
+ ///
+ /// Conventional Id Key property
+ ///
+ public int Id { get; set; }
+ public string Name { get; set; }
+ }
+
+ public class Hippo
+ {
+ ///
+ /// Conventional {TypeName + Id} Key Property
+ ///
+ public int HippoId { get; set; }
+ public double Weight { get; set; }
+ }
+
+ public class Ferret
+ {
+ ///
+ /// Conventional {TypeName + Id} Key Property with odd casing
+ ///
+ public int FeRReTID { get; set; }
+ public double Weight { get; set; }
+ }
+
+ public class Lion
+ {
+ public int SomeId { get; set; }
+ public string Name { get; set; }
+ }
}
}