Skip to content

Commit

Permalink
Support Enums as Keys in OData Client (#3013)
Browse files Browse the repository at this point in the history
* Checkout from 'main'

* Add and modify test cases

* Only allow nullable Generic Type Enum

* Adding a test that throws Exception if key is nullable generic type with struct as generic argument

* Adding a test to mock querying data with enum as keys

* Add a test to select single entity

* Add test to filter by enum member name and a test to use ByKey to filter by composite keys

* Add a test to filter by enum key only using ByKey()
  • Loading branch information
WanjohiSammy authored Jul 22, 2024
1 parent 3a51fa0 commit c313eae
Show file tree
Hide file tree
Showing 3 changed files with 469 additions and 1 deletion.
4 changes: 3 additions & 1 deletion src/Microsoft.OData.Client/Metadata/ODataTypeInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,9 @@ private PropertyInfo[] GetKeyProperties()
throw c.Error.InvalidOperation(c.Strings.ClientType_KeysOnDifferentDeclaredType(typeName));
}

if (!PrimitiveType.IsKnownType(key.PropertyType) && !(key.PropertyType.GetGenericTypeDefinition() == typeof(System.Nullable<>) && key.PropertyType.GetGenericArguments().First().IsEnum()))
// Check if the key property's type is a known primitive, an enum, or a nullable generic.
// If it doesn't meet any of these conditions, throw an InvalidOperationException.
if (!PrimitiveType.IsKnownType(key.PropertyType) && !key.PropertyType.IsEnum() && !(key.PropertyType.IsGenericType() && key.PropertyType.GetGenericTypeDefinition() == typeof(System.Nullable<>) && key.PropertyType.GetGenericArguments().First().IsEnum()))
{
throw c.Error.InvalidOperation(c.Strings.ClientType_KeysMustBeSimpleTypes(key.Name, typeName, key.PropertyType.FullName));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

using Microsoft.OData.Client.Metadata;
using System;
using System.Linq;
using System.Reflection;
using Xunit;

namespace Microsoft.OData.Client.Tests.Metadata
Expand Down Expand Up @@ -50,6 +52,99 @@ public void IFTypeProperty_HasKeyAttributeAndOneProperty_TypeIsEntityAndDoesNotT
Assert.True(actualResult);
}

[Fact]
public void IFType_HasMultipleKeyAttributesWhereOneIsEnum_TypeIsEntityAndDoesNotThrowException()
{
//Arrange
Type employee = typeof(Employee);

//Act
bool actualResult = ClientTypeUtil.TypeOrElementTypeIsEntity(employee);

//Assert
Assert.True(actualResult);
}

[Fact]
public void IFTypeProperty_HasMultipleKeyAttributes_GetKeyPropertiesOnType_DoesNotThrowException()
{
//Arrange
Type employee = typeof(Employee);

int expectedNumberOfKeyProperties = 4; // 2 Primitive Known Types, 1 Enum Type, 1 Enum Nullable Generic Type

//Act
PropertyInfo[] keyProperties = ClientTypeUtil.GetKeyPropertiesOnType(employee);

//Assert
Assert.Equal(expectedNumberOfKeyProperties, keyProperties.Length);
}

[Fact]
public void IFTypeProperty_HasEnumTypeKeyAttribute_GetKeyPropertiesOnType_DoesNotThrowException()
{
// Arrange
Type employee = typeof(Employee);

//Act
PropertyInfo[] keyProperties = ClientTypeUtil.GetKeyPropertiesOnType(employee);
PropertyInfo key = keyProperties.Single(k => k.Name == "EmpType");

//Assert
Assert.True(key.PropertyType.IsEnum());
Assert.True(key.PropertyType == typeof(EmployeeType));
}

[Fact]
public void IFTypeProperty_HasKnownPrimitiveTypesKeyAttributes_GetKeyPropertiesOnType_DoesNotThrowException()
{
// Arrange
Type employee = typeof(Employee);

//Act
PropertyInfo[] keyProperties = ClientTypeUtil.GetKeyPropertiesOnType(employee);

PropertyInfo empNumKey = keyProperties.Single(k => k.Name == "EmpNumber");
PropertyInfo deptNumKey = keyProperties.Single(k => k.Name == "DeptNumber");

//Assert
Assert.True(PrimitiveType.IsKnownType(empNumKey.PropertyType) && empNumKey.PropertyType == typeof(int));
Assert.True(PrimitiveType.IsKnownType(deptNumKey.PropertyType) && deptNumKey.PropertyType == typeof(string));
}

[Fact]
public void IFTypeProperty_HasNullableGenericTypeKeyAttribute_OfTypeEnum_GetKeyPropertiesOnType_DoesNotThrowException()
{
// Arrange
Type employee = typeof(Employee);

//Act
PropertyInfo[] keyProperties = ClientTypeUtil.GetKeyPropertiesOnType(employee);
PropertyInfo key = keyProperties.Single(k => k.Name == "NullableEmpType");

//Assert
Assert.True(key.PropertyType.IsGenericType);
Assert.True(key.PropertyType == typeof(System.Nullable<EmployeeType>));
}

[Fact]
public void IFTypeProperty_HasNullableGenericTypeKey_OfTypeStruct_GetKeyPropertiesOnType_ThrowsException()
{
// Arrange
Type employee = typeof(EmployeeWithNullableStruct);

PropertyInfo empTypeStructKey = employee.GetProperty("EmpTypeStruct");

InvalidOperationException expectedException = Error.InvalidOperation(Strings.ClientType_KeysMustBeSimpleTypes(empTypeStructKey.Name, employee.ToString(), empTypeStructKey.PropertyType.FullName));

//Act
InvalidOperationException actualException = Assert.Throws<InvalidOperationException>(() => ClientTypeUtil.GetKeyPropertiesOnType(employee));

//Assert
Assert.NotNull(actualException);
Assert.Equal(expectedException.Message, actualException.Message);
}

public class Person
{
[System.ComponentModel.DataAnnotations.Key]
Expand All @@ -69,5 +164,55 @@ public class Car
public int NonStandardId { get; set; }
}

public class Employee
{
[System.ComponentModel.DataAnnotations.Key]
public int EmpNumber { get; set; }

[System.ComponentModel.DataAnnotations.Key]
public string DeptNumber { get; set; }

[System.ComponentModel.DataAnnotations.Key]
public EmployeeType EmpType { get; set; }

[System.ComponentModel.DataAnnotations.Key]
public EmployeeType? NullableEmpType { get; set; }

public string Name { get; set; }

[System.ComponentModel.DataAnnotations.Schema.ForeignKey("DeptNumber")]
public Department Department { get; set; }
}

public class EmployeeWithNullableStruct
{
[System.ComponentModel.DataAnnotations.Key]
public int EmpNumber { get; set; }

[System.ComponentModel.DataAnnotations.Key]
public EmployeeTypeStruct? EmpTypeStruct { get; set; }

public string Name { get; set; }
}

public enum EmployeeType
{
None = 1,
FullTime = 2,
PartTime = 3
}

public class Department
{
[System.ComponentModel.DataAnnotations.Key]
public string DeptId { get; set; }
public string Name { get; set; }
}

public struct EmployeeTypeStruct
{
public int EmpTypeId { get; set; }
}

}
}
Loading

0 comments on commit c313eae

Please sign in to comment.