Skip to content

Commit

Permalink
Fixes #3052: Update ODataTypeInfo.cs not correctly identifying entity…
Browse files Browse the repository at this point in the history
… keys by Id property
  • Loading branch information
lawynn authored and xuzhg committed Oct 28, 2024
1 parent 7ebf52a commit b84465b
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 15 deletions.
18 changes: 17 additions & 1 deletion src/Microsoft.OData.Client/Metadata/ClientTypeUtil.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// <copyright file="ClientTypeUtil.cs" company="Microsoft">
// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
// </copyright>
Expand All @@ -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.

Expand Down Expand Up @@ -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;
}
}
}
32 changes: 19 additions & 13 deletions src/Microsoft.OData.Client/Metadata/ODataTypeInfo.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// <copyright file="ODataTypeInfo.cs" company="Microsoft">
// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
// </copyright>
Expand Down Expand Up @@ -46,7 +46,7 @@ internal class ODataTypeInfo
public ODataTypeInfo(Type type)
{
this.type = type;
ServerSideNameDict = new ConcurrentDictionary<string, string>();
ServerSideNameDict = new ConcurrentDictionary<string, string>();
}

/// <summary>
Expand Down Expand Up @@ -84,8 +84,8 @@ public bool HasProperties
/// <summary>
/// Sertver defined type name
/// </summary>
public string ServerDefinedTypeName
{
public string ServerDefinedTypeName
{
get
{
if (_serverDefinedTypeName == null)
Expand All @@ -102,7 +102,7 @@ public string ServerDefinedTypeName
else
{
_serverDefinedTypeName = type.Name;
}
}
}

return _serverDefinedTypeName;
Expand Down Expand Up @@ -251,9 +251,9 @@ private IEnumerable<PropertyInfo> 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) &&
Expand Down Expand Up @@ -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<string>();
select a).First<string>();
throw Client.Error.InvalidOperation(Client.Strings.ClientType_MissingProperty(typeName, m));
}
}
Expand Down Expand Up @@ -381,10 +381,16 @@ private static void InserKeyBasedOnOrder(List<KeyValuePair<PropertyInfo, int>> 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))
{
Expand All @@ -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;
Expand All @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// <copyright file="ClientTypeUtilTests.cs" company="Microsoft">
// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
// </copyright>
Expand All @@ -17,6 +17,19 @@ namespace Microsoft.OData.Client.Tests.Metadata
/// </summary>
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()
{
Expand Down Expand Up @@ -214,5 +227,37 @@ public struct EmployeeTypeStruct
public int EmpTypeId { get; set; }
}

public class Giraffe
{
/// <summary>
/// Conventional Id Key property
/// </summary>
public int Id { get; set; }
public string Name { get; set; }
}

public class Hippo
{
/// <summary>
/// Conventional {TypeName + Id} Key Property
/// </summary>
public int HippoId { get; set; }
public double Weight { get; set; }
}

public class Ferret
{
/// <summary>
/// Conventional {TypeName + Id} Key Property with odd casing
/// </summary>
public int FeRReTID { get; set; }
public double Weight { get; set; }
}

public class Lion
{
public int SomeId { get; set; }
public string Name { get; set; }
}
}
}

0 comments on commit b84465b

Please sign in to comment.