From ac81804e697470925449ba8f2dc6a80bbd6d412a Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Thu, 4 Jul 2024 13:15:30 +0300 Subject: [PATCH 1/8] Checkout from 'main' --- .../Metadata/ODataTypeInfo.cs | 4 +- .../Metadata/ClientTypeUtilTests.cs | 117 ++++++++++++++++++ 2 files changed, 120 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.OData.Client/Metadata/ODataTypeInfo.cs b/src/Microsoft.OData.Client/Metadata/ODataTypeInfo.cs index d94e62c75e..338e2c5392 100644 --- a/src/Microsoft.OData.Client/Metadata/ODataTypeInfo.cs +++ b/src/Microsoft.OData.Client/Metadata/ODataTypeInfo.cs @@ -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<>))) { throw c.Error.InvalidOperation(c.Strings.ClientType_KeysMustBeSimpleTypes(key.Name, typeName, key.PropertyType.FullName)); } diff --git a/test/FunctionalTests/Microsoft.OData.Client.Tests/Metadata/ClientTypeUtilTests.cs b/test/FunctionalTests/Microsoft.OData.Client.Tests/Metadata/ClientTypeUtilTests.cs index 3fa39e6981..4d4b12f935 100644 --- a/test/FunctionalTests/Microsoft.OData.Client.Tests/Metadata/ClientTypeUtilTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Client.Tests/Metadata/ClientTypeUtilTests.cs @@ -6,6 +6,7 @@ using Microsoft.OData.Client.Metadata; using System; +using System.Linq; using Xunit; namespace Microsoft.OData.Client.Tests.Metadata @@ -50,6 +51,86 @@ 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 Nullable Generic Type + + //Act + var keyProperties = ClientTypeUtil.GetKeyPropertiesOnType(employee); + + //Assert + Assert.Equal(expectedNumberOfKeyProperties, keyProperties.Length); + } + + [Fact] + public void IFTypeProperty_HasEnumTypeKeyAttribute_GetKeyPropertiesOnType_DoesNotThrowException() + { + // Arrange + Type employee = typeof(Employee); + + //Act + var keyProperties = ClientTypeUtil.GetKeyPropertiesOnType(employee); + + //Assert + Assert.Equal("EmpType", keyProperties.Single(k => k.PropertyType == typeof(EmployeeType)).Name); + } + + [Fact] + public void IFTypeProperty_HasKnownPrimitiveTypesKeyAttributes_GetKeyPropertiesOnType_DoesNotThrowException() + { + // Arrange + Type employee = typeof(Employee); + + //Act + var keyProperties = ClientTypeUtil.GetKeyPropertiesOnType(employee); + + //Assert + foreach (var keyProperty in keyProperties) + { + if (PrimitiveType.IsKnownType(keyProperty.PropertyType)) + { + if (keyProperty.PropertyType == typeof(int)) + { + Assert.Equal("EmpNumber", keyProperty.Name); + } + else if (keyProperty.PropertyType == typeof(string)) + { + Assert.Equal("DeptNumber", keyProperty.Name); + } + } + } + } + + [Fact] + public void IFTypeProperty_HasNullableGenericTypeKeyAttribute_GetKeyPropertiesOnType_DoesNotThrowException() + { + // Arrange + Type employee = typeof(Employee); + + //Act + var keyProperties = ClientTypeUtil.GetKeyPropertiesOnType(employee); + + //Assert + Assert.Equal("NullableId", keyProperties.Single(k => k.PropertyType == typeof(System.Nullable)).Name); + } + public class Person { [System.ComponentModel.DataAnnotations.Key] @@ -69,5 +150,41 @@ 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 int? NullableId { get; set; } + + public string Name { get; set; } + + public decimal Salary { get; set; } + + [System.ComponentModel.DataAnnotations.Schema.ForeignKey("DeptNumber")] + public Department Department { 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; } + } + } } From e94b072900f3c54593f58934ea14c1b81978b26d Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Thu, 4 Jul 2024 15:00:44 +0300 Subject: [PATCH 2/8] Add and modify test cases --- .../Metadata/ClientTypeUtilTests.cs | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/test/FunctionalTests/Microsoft.OData.Client.Tests/Metadata/ClientTypeUtilTests.cs b/test/FunctionalTests/Microsoft.OData.Client.Tests/Metadata/ClientTypeUtilTests.cs index 4d4b12f935..b8f14f0a2d 100644 --- a/test/FunctionalTests/Microsoft.OData.Client.Tests/Metadata/ClientTypeUtilTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Client.Tests/Metadata/ClientTypeUtilTests.cs @@ -87,9 +87,11 @@ public void IFTypeProperty_HasEnumTypeKeyAttribute_GetKeyPropertiesOnType_DoesNo //Act var keyProperties = ClientTypeUtil.GetKeyPropertiesOnType(employee); + var key = keyProperties.Single(k => k.Name == "EmpType"); //Assert - Assert.Equal("EmpType", keyProperties.Single(k => k.PropertyType == typeof(EmployeeType)).Name); + Assert.True(key.PropertyType.IsEnum()); + Assert.True(key.PropertyType == typeof(EmployeeType)); } [Fact] @@ -101,21 +103,12 @@ public void IFTypeProperty_HasKnownPrimitiveTypesKeyAttributes_GetKeyPropertiesO //Act var keyProperties = ClientTypeUtil.GetKeyPropertiesOnType(employee); + var empNumKey = keyProperties.Single(k => k.Name == "EmpNumber"); + var deptNumKey = keyProperties.Single(k => k.Name == "DeptNumber"); + //Assert - foreach (var keyProperty in keyProperties) - { - if (PrimitiveType.IsKnownType(keyProperty.PropertyType)) - { - if (keyProperty.PropertyType == typeof(int)) - { - Assert.Equal("EmpNumber", keyProperty.Name); - } - else if (keyProperty.PropertyType == typeof(string)) - { - Assert.Equal("DeptNumber", keyProperty.Name); - } - } - } + Assert.True(PrimitiveType.IsKnownType(empNumKey.PropertyType) && empNumKey.PropertyType == typeof(int)); + Assert.True(PrimitiveType.IsKnownType(deptNumKey.PropertyType) && deptNumKey.PropertyType == typeof(string)); } [Fact] @@ -126,9 +119,11 @@ public void IFTypeProperty_HasNullableGenericTypeKeyAttribute_GetKeyPropertiesOn //Act var keyProperties = ClientTypeUtil.GetKeyPropertiesOnType(employee); + var key = keyProperties.Single(k => k.Name == "NullableId"); //Assert - Assert.Equal("NullableId", keyProperties.Single(k => k.PropertyType == typeof(System.Nullable)).Name); + Assert.True(key.PropertyType.IsGenericType); + Assert.True(key.PropertyType == typeof(System.Nullable)); } public class Person From 2bcef171f6c70dcea152904720cef68b091441fa Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Fri, 12 Jul 2024 01:14:59 +0300 Subject: [PATCH 3/8] Only allow nullable Generic Type Enum --- .../Metadata/ODataTypeInfo.cs | 2 +- .../Metadata/ClientTypeUtilTests.cs | 25 ++++++++++--------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/Microsoft.OData.Client/Metadata/ODataTypeInfo.cs b/src/Microsoft.OData.Client/Metadata/ODataTypeInfo.cs index 338e2c5392..9b8666c585 100644 --- a/src/Microsoft.OData.Client/Metadata/ODataTypeInfo.cs +++ b/src/Microsoft.OData.Client/Metadata/ODataTypeInfo.cs @@ -323,7 +323,7 @@ private PropertyInfo[] GetKeyProperties() // 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<>))) + 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)); } diff --git a/test/FunctionalTests/Microsoft.OData.Client.Tests/Metadata/ClientTypeUtilTests.cs b/test/FunctionalTests/Microsoft.OData.Client.Tests/Metadata/ClientTypeUtilTests.cs index b8f14f0a2d..7c04d1761f 100644 --- a/test/FunctionalTests/Microsoft.OData.Client.Tests/Metadata/ClientTypeUtilTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Client.Tests/Metadata/ClientTypeUtilTests.cs @@ -7,6 +7,7 @@ using Microsoft.OData.Client.Metadata; using System; using System.Linq; +using System.Reflection; using Xunit; namespace Microsoft.OData.Client.Tests.Metadata @@ -70,10 +71,10 @@ public void IFTypeProperty_HasMultipleKeyAttributes_GetKeyPropertiesOnType_DoesN //Arrange Type employee = typeof(Employee); - int expectedNumberOfKeyProperties = 4; // 2 Primitive Known Types, 1 Enum Type, 1 Nullable Generic Type + int expectedNumberOfKeyProperties = 4; // 2 Primitive Known Types, 1 Enum Type, 1 Enum Nullable Generic Type //Act - var keyProperties = ClientTypeUtil.GetKeyPropertiesOnType(employee); + PropertyInfo[] keyProperties = ClientTypeUtil.GetKeyPropertiesOnType(employee); //Assert Assert.Equal(expectedNumberOfKeyProperties, keyProperties.Length); @@ -86,8 +87,8 @@ public void IFTypeProperty_HasEnumTypeKeyAttribute_GetKeyPropertiesOnType_DoesNo Type employee = typeof(Employee); //Act - var keyProperties = ClientTypeUtil.GetKeyPropertiesOnType(employee); - var key = keyProperties.Single(k => k.Name == "EmpType"); + PropertyInfo[] keyProperties = ClientTypeUtil.GetKeyPropertiesOnType(employee); + PropertyInfo key = keyProperties.Single(k => k.Name == "EmpType"); //Assert Assert.True(key.PropertyType.IsEnum()); @@ -101,10 +102,10 @@ public void IFTypeProperty_HasKnownPrimitiveTypesKeyAttributes_GetKeyPropertiesO Type employee = typeof(Employee); //Act - var keyProperties = ClientTypeUtil.GetKeyPropertiesOnType(employee); + PropertyInfo[] keyProperties = ClientTypeUtil.GetKeyPropertiesOnType(employee); - var empNumKey = keyProperties.Single(k => k.Name == "EmpNumber"); - var deptNumKey = keyProperties.Single(k => k.Name == "DeptNumber"); + 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)); @@ -112,18 +113,18 @@ public void IFTypeProperty_HasKnownPrimitiveTypesKeyAttributes_GetKeyPropertiesO } [Fact] - public void IFTypeProperty_HasNullableGenericTypeKeyAttribute_GetKeyPropertiesOnType_DoesNotThrowException() + public void IFTypeProperty_HasNullableGenericTypeKeyAttributeOfTypeEnum_GetKeyPropertiesOnType_DoesNotThrowException() { // Arrange Type employee = typeof(Employee); //Act - var keyProperties = ClientTypeUtil.GetKeyPropertiesOnType(employee); - var key = keyProperties.Single(k => k.Name == "NullableId"); + 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)); + Assert.True(key.PropertyType == typeof(System.Nullable)); } public class Person @@ -157,7 +158,7 @@ public class Employee public EmployeeType EmpType { get; set; } [System.ComponentModel.DataAnnotations.Key] - public int? NullableId { get; set; } + public EmployeeType? NullableEmpType { get; set; } public string Name { get; set; } From 021fad10ee8d8e80d587a79f3152391a3e4eb041 Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Fri, 12 Jul 2024 09:28:02 +0300 Subject: [PATCH 4/8] Adding a test that throws Exception if key is nullable generic type with struct as generic argument --- .../Metadata/ClientTypeUtilTests.cs | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/test/FunctionalTests/Microsoft.OData.Client.Tests/Metadata/ClientTypeUtilTests.cs b/test/FunctionalTests/Microsoft.OData.Client.Tests/Metadata/ClientTypeUtilTests.cs index 7c04d1761f..db997409d5 100644 --- a/test/FunctionalTests/Microsoft.OData.Client.Tests/Metadata/ClientTypeUtilTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Client.Tests/Metadata/ClientTypeUtilTests.cs @@ -113,7 +113,7 @@ public void IFTypeProperty_HasKnownPrimitiveTypesKeyAttributes_GetKeyPropertiesO } [Fact] - public void IFTypeProperty_HasNullableGenericTypeKeyAttributeOfTypeEnum_GetKeyPropertiesOnType_DoesNotThrowException() + public void IFTypeProperty_HasNullableGenericTypeKeyAttribute_OfTypeEnum_GetKeyPropertiesOnType_DoesNotThrowException() { // Arrange Type employee = typeof(Employee); @@ -127,6 +127,24 @@ public void IFTypeProperty_HasNullableGenericTypeKeyAttributeOfTypeEnum_GetKeyPr Assert.True(key.PropertyType == typeof(System.Nullable)); } + [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(() => ClientTypeUtil.GetKeyPropertiesOnType(employee)); + + //Assert + Assert.NotNull(actualException); + Assert.Equal(expectedException.Message, actualException.Message); + } + public class Person { [System.ComponentModel.DataAnnotations.Key] @@ -162,12 +180,21 @@ public class Employee public string Name { get; set; } - public decimal Salary { 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, @@ -182,5 +209,10 @@ public class Department public string Name { get; set; } } + public struct EmployeeTypeStruct + { + public int EmpTypeId { get; set; } + } + } } From e67b52e6d8547d442f3b3b324103451393bc6507 Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Mon, 15 Jul 2024 19:47:49 +0300 Subject: [PATCH 5/8] Adding a test to mock querying data with enum as keys --- .../Tracking/DataServiceContextQueryTests.cs | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 test/FunctionalTests/Microsoft.OData.Client.Tests/Tracking/DataServiceContextQueryTests.cs diff --git a/test/FunctionalTests/Microsoft.OData.Client.Tests/Tracking/DataServiceContextQueryTests.cs b/test/FunctionalTests/Microsoft.OData.Client.Tests/Tracking/DataServiceContextQueryTests.cs new file mode 100644 index 0000000000..4c95e2be16 --- /dev/null +++ b/test/FunctionalTests/Microsoft.OData.Client.Tests/Tracking/DataServiceContextQueryTests.cs @@ -0,0 +1,161 @@ +//--------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. +// +//--------------------------------------------------------------------- + +using Microsoft.OData.Edm.Csdl; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System.Xml; +using Xunit; + +namespace Microsoft.OData.Client.Tests.Tracking +{ + public class DataServiceContextQueryTests + { + private readonly Container _defaultContext; + + #region Test Edmx + private const string Edmx = @" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"; + #endregion + + public DataServiceContextQueryTests() + { + var uri = new Uri("http://localhost:8000"); + _defaultContext = new Container(uri); + } + + [Fact] + public async Task SelectEntities_WithEnumAsKey_DoNotThrowException() + { + // Arrange + string response = @"{ + ""@odata.context"": ""http://localhost:5128/$metadata#Employees"", + ""value"": [ + { + ""EmpNumber"": 1, + ""EmpType"": ""FullTime"", + ""OrgId"": 1, + ""Name"": ""John Doe"" + }, + { + ""EmpNumber"": 2, + ""EmpType"": ""PartTime"", + ""OrgId"": 1, + ""Name"": ""Jane Doe"" + } + ] +}"; + SetupContextWithRequestPipeline(new DataServiceContext[] { _defaultContext }, response, "http://localhost:8000/employees"); + + // Act + IEnumerable employees = await _defaultContext.Employees.ExecuteAsync(); + + // Assert + Assert.Equal(2, employees.Count()); + } + + private void SetupContextWithRequestPipeline(DataServiceContext[] contexts, string response, string location) + { + foreach (var context in contexts) + { + context.Configurations.RequestPipeline.OnMessageCreating = + (args) => new CustomizedRequestMessage( + args, + response, + new Dictionary() + { + { "Content-Type", "application/json;charset=utf-8" }, + { "Location", location }, + }); + } + } + + class Container : DataServiceContext + { + public Container(Uri serviceRoot) : + base(serviceRoot, ODataProtocolVersion.V4) + { + Format.LoadServiceModel = () => CsdlReader.Parse(XmlReader.Create(new StringReader(Edmx))); + Format.UseJson(); + Employees = base.CreateQuery("Employees"); + } + + public DataServiceQuery Employees { get; private set; } + } + } + + [Key("EmpNumber", "EmpType", "OrgId")] + public class Employee : BaseEntityType + { + public int EmpNumber { get; set; } + + // Enum - Employee Type Key + public EmployeeType EmpType { get; set; } + + public int OrgId { get; set; } + + public string Name { get; set; } + + [ForeignKey("OrgId")] + public virtual Organization Organization { get; set; } + } + + public class Organization + { + public int Id { get; set; } + public string Name { get; set; } + } + + public enum EmployeeType + { + None = 1, + FullTime = 2, + PartTime = 3 + } +} From cee9572963a2c6413cdc9dea9c68f348feca0422 Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Mon, 15 Jul 2024 22:17:16 +0300 Subject: [PATCH 6/8] Add a test to select single entity --- .../Tracking/DataServiceContextQueryTests.cs | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/test/FunctionalTests/Microsoft.OData.Client.Tests/Tracking/DataServiceContextQueryTests.cs b/test/FunctionalTests/Microsoft.OData.Client.Tests/Tracking/DataServiceContextQueryTests.cs index 4c95e2be16..d092d7ad3b 100644 --- a/test/FunctionalTests/Microsoft.OData.Client.Tests/Tracking/DataServiceContextQueryTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Client.Tests/Tracking/DataServiceContextQueryTests.cs @@ -1,5 +1,5 @@ //--------------------------------------------------------------------- -// +// // 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.Tests.Tracking { public class DataServiceContextQueryTests { + private const string ServiceRoot = "http://localhost:8007"; private readonly Container _defaultContext; #region Test Edmx @@ -66,7 +67,7 @@ public class DataServiceContextQueryTests public DataServiceContextQueryTests() { - var uri = new Uri("http://localhost:8000"); + var uri = new Uri(ServiceRoot); _defaultContext = new Container(uri); } @@ -75,7 +76,7 @@ public async Task SelectEntities_WithEnumAsKey_DoNotThrowException() { // Arrange string response = @"{ - ""@odata.context"": ""http://localhost:5128/$metadata#Employees"", + ""@odata.context"": ""http://localhost:8007/$metadata#Employees"", ""value"": [ { ""EmpNumber"": 1, @@ -91,7 +92,7 @@ public async Task SelectEntities_WithEnumAsKey_DoNotThrowException() } ] }"; - SetupContextWithRequestPipeline(new DataServiceContext[] { _defaultContext }, response, "http://localhost:8000/employees"); + SetupContextWithRequestPipeline(new DataServiceContext[] { _defaultContext }, response, "employees"); // Act IEnumerable employees = await _defaultContext.Employees.ExecuteAsync(); @@ -100,8 +101,35 @@ public async Task SelectEntities_WithEnumAsKey_DoNotThrowException() Assert.Equal(2, employees.Count()); } - private void SetupContextWithRequestPipeline(DataServiceContext[] contexts, string response, string location) + [Fact] + public void SelectSpecificEntity_WithEnumAsKey_DoNotThrowException() + { + // Arrange + string response = @"{ + ""@odata.context"": ""http://localhost:8007/$metadata#Employees"", + ""value"": [ + { + ""EmpNumber"": 8, + ""EmpType"": ""PartTime"", + ""OrgId"": 1, + ""Name"": ""Employee Two"" + } + ] +}"; + SetupContextWithRequestPipeline(new DataServiceContext[] { _defaultContext }, response, "employees"); + + // Act + Employee employee = _defaultContext.Employees.Where(e => e.EmpNumber == 8).First(); + + // Assert + Assert.Equal(EmployeeType.PartTime, employee.EmpType); + Assert.Equal("Employee Two", employee.Name); + } + + private void SetupContextWithRequestPipeline(DataServiceContext[] contexts, string response, string path) { + string location = $"{ServiceRoot}/{path}"; + foreach (var context in contexts) { context.Configurations.RequestPipeline.OnMessageCreating = From 04d79b127d87e6222d99cec5f14c6ae27cc39b78 Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Tue, 16 Jul 2024 14:36:04 +0300 Subject: [PATCH 7/8] Add test to filter by enum member name and a test to use ByKey to filter by composite keys --- .../Tracking/DataServiceContextQueryTests.cs | 95 ++++++++++++++++++- 1 file changed, 90 insertions(+), 5 deletions(-) diff --git a/test/FunctionalTests/Microsoft.OData.Client.Tests/Tracking/DataServiceContextQueryTests.cs b/test/FunctionalTests/Microsoft.OData.Client.Tests/Tracking/DataServiceContextQueryTests.cs index d092d7ad3b..77bd5e538a 100644 --- a/test/FunctionalTests/Microsoft.OData.Client.Tests/Tracking/DataServiceContextQueryTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Client.Tests/Tracking/DataServiceContextQueryTests.cs @@ -75,6 +75,8 @@ public DataServiceContextQueryTests() public async Task SelectEntities_WithEnumAsKey_DoNotThrowException() { // Arrange + var expectedUri = $"{ServiceRoot}/Employees"; + string response = @"{ ""@odata.context"": ""http://localhost:8007/$metadata#Employees"", ""value"": [ @@ -92,19 +94,23 @@ public async Task SelectEntities_WithEnumAsKey_DoNotThrowException() } ] }"; - SetupContextWithRequestPipeline(new DataServiceContext[] { _defaultContext }, response, "employees"); + SetupContextWithRequestPipeline(new DataServiceContext[] { _defaultContext }, response, "Employees"); // Act - IEnumerable employees = await _defaultContext.Employees.ExecuteAsync(); + DataServiceQuery query = _defaultContext.Employees; + IEnumerable employees = await query.ExecuteAsync(); // Assert + Assert.Equal(expectedUri, query.ToString()); Assert.Equal(2, employees.Count()); } [Fact] - public void SelectSpecificEntity_WithEnumAsKey_DoNotThrowException() + public void UseWhereToFilterByEmployeeNumberKey_WithEnumAsKey_DoNotThrowException() { // Arrange + string expectedUri = $"{ServiceRoot}/Employees?$filter=EmpNumber eq 8"; + string response = @"{ ""@odata.context"": ""http://localhost:8007/$metadata#Employees"", ""value"": [ @@ -116,16 +122,78 @@ public void SelectSpecificEntity_WithEnumAsKey_DoNotThrowException() } ] }"; - SetupContextWithRequestPipeline(new DataServiceContext[] { _defaultContext }, response, "employees"); + SetupContextWithRequestPipeline(new DataServiceContext[] { _defaultContext }, response, "Employees"); // Act - Employee employee = _defaultContext.Employees.Where(e => e.EmpNumber == 8).First(); + IQueryable query = _defaultContext.Employees.Where(e => e.EmpNumber == 8); + Employee employee = query.First(); // Assert + Assert.Equal(expectedUri, query.ToString()); Assert.Equal(EmployeeType.PartTime, employee.EmpType); Assert.Equal("Employee Two", employee.Name); } + [Fact] + public void UseWhereToFilterByEnumKey_WithEnumAsKey_DoNotThrowException() + { + // Arrange + string expectedUri = $"{ServiceRoot}/Employees?$filter=EmpType eq Microsoft.OData.Client.Tests.Tracking.EmployeeType'PartTime'"; + + string response = @"{ + ""@odata.context"": ""http://localhost:8007/$metadata#Employees"", + ""value"": [ + { + ""EmpNumber"": 8, + ""EmpType"": ""PartTime"", + ""OrgId"": 1, + ""Name"": ""Employee 45"" + } + ] +}"; + SetupContextWithRequestPipeline(new DataServiceContext[] { _defaultContext }, response, "Employees"); + + // Act + var query = _defaultContext.Employees.Where(e => e.EmpType == EmployeeType.PartTime); + Employee employee = query.First(); + + // Assert + Assert.Equal(expectedUri, query.ToString()); + Assert.Equal(8, employee.EmpNumber); + Assert.Equal(EmployeeType.PartTime, employee.EmpType); + } + + [Fact] + public void UseByKeyToFilterByCompositeKeys_WithEnumAsKey_DoNotThrowException() + { + // Arrange + string expectedUri = $"{ServiceRoot}/Employees(EmpNumber=8,EmpType=Microsoft.OData.Client.Tests.Tracking.EmployeeType'PartTime',OrgId=1)"; + + string response = @"{ + ""@odata.context"": ""http://localhost:8007/$metadata#Employees"", + ""value"": [ + { + ""EmpNumber"": 8, + ""EmpType"": ""PartTime"", + ""OrgId"": 1, + ""Name"": ""Employee 24"" + } + ] +}"; + SetupContextWithRequestPipeline(new DataServiceContext[] { _defaultContext }, response, "Employees"); + + // Act + EmployeeSingle query = _defaultContext.Employees.ByKey( + new Dictionary() { { "EmpNumber", 8 }, { "EmpType", EmployeeType.PartTime }, { "OrgId", 1 } }); + + Employee employee = query.GetValue(); + + // Assert + Assert.Equal(expectedUri, query.Query.ToString()); + Assert.Equal("Employee 24", employee.Name); + Assert.Equal(EmployeeType.PartTime, employee.EmpType); + } + private void SetupContextWithRequestPipeline(DataServiceContext[] contexts, string response, string path) { string location = $"{ServiceRoot}/{path}"; @@ -186,4 +254,21 @@ public enum EmployeeType FullTime = 2, PartTime = 3 } + + public partial class EmployeeSingle : DataServiceQuerySingle + { + /// + /// Initialize a new EmployeeSingle object. + /// + public EmployeeSingle(DataServiceContext context, string path) + : base(context, path) { } + } + + public static class ExtensionMethods + { + public static EmployeeSingle ByKey(this DataServiceQuery _source, IDictionary _keys) + { + return new EmployeeSingle(_source.Context, _source.GetKeyPath(Serializer.GetKeyString(_source.Context, _keys))); + } + } } From 6a5667f941b8fbe9d10929ef72909495775f2582 Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Fri, 19 Jul 2024 18:24:05 +0300 Subject: [PATCH 8/8] Add a test to filter by enum key only using ByKey() --- .../Tracking/DataServiceContextQueryTests.cs | 87 ++++++++++++++----- 1 file changed, 67 insertions(+), 20 deletions(-) diff --git a/test/FunctionalTests/Microsoft.OData.Client.Tests/Tracking/DataServiceContextQueryTests.cs b/test/FunctionalTests/Microsoft.OData.Client.Tests/Tracking/DataServiceContextQueryTests.cs index 77bd5e538a..46c042354f 100644 --- a/test/FunctionalTests/Microsoft.OData.Client.Tests/Tracking/DataServiceContextQueryTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Client.Tests/Tracking/DataServiceContextQueryTests.cs @@ -94,7 +94,11 @@ public async Task SelectEntities_WithEnumAsKey_DoNotThrowException() } ] }"; - SetupContextWithRequestPipeline(new DataServiceContext[] { _defaultContext }, response, "Employees"); + SetupContextWithRequestPipeline(_defaultContext, response, "Employees"); + _defaultContext.SendingRequest2 += (sender, args) => + { + Assert.Equal(expectedUri, args.RequestMessage.Url.ToString()); + }; // Act DataServiceQuery query = _defaultContext.Employees; @@ -106,7 +110,7 @@ public async Task SelectEntities_WithEnumAsKey_DoNotThrowException() } [Fact] - public void UseWhereToFilterByEmployeeNumberKey_WithEnumAsKey_DoNotThrowException() + public void UseWhereToFilterByOtherKeyOtherThanEnumKey_WithEnumAsKey_DoNotThrowException() { // Arrange string expectedUri = $"{ServiceRoot}/Employees?$filter=EmpNumber eq 8"; @@ -122,7 +126,11 @@ public void UseWhereToFilterByEmployeeNumberKey_WithEnumAsKey_DoNotThrowExceptio } ] }"; - SetupContextWithRequestPipeline(new DataServiceContext[] { _defaultContext }, response, "Employees"); + SetupContextWithRequestPipeline(_defaultContext, response, "Employees"); + _defaultContext.SendingRequest2 += (sender, args) => + { + Assert.Equal($"{expectedUri}&$top=1", args.RequestMessage.Url.ToString()); + }; // Act IQueryable query = _defaultContext.Employees.Where(e => e.EmpNumber == 8); @@ -151,7 +159,11 @@ public void UseWhereToFilterByEnumKey_WithEnumAsKey_DoNotThrowException() } ] }"; - SetupContextWithRequestPipeline(new DataServiceContext[] { _defaultContext }, response, "Employees"); + SetupContextWithRequestPipeline(_defaultContext, response, "Employees"); + _defaultContext.SendingRequest2 += (sender, args) => + { + Assert.Equal($"{expectedUri}&$top=1", args.RequestMessage.Url.ToString()); + }; // Act var query = _defaultContext.Employees.Where(e => e.EmpType == EmployeeType.PartTime); @@ -164,7 +176,7 @@ public void UseWhereToFilterByEnumKey_WithEnumAsKey_DoNotThrowException() } [Fact] - public void UseByKeyToFilterByCompositeKeys_WithEnumAsKey_DoNotThrowException() + public async Task FilterByCompositeKeys_WithEnumAsKey_DoNotThrowException() { // Arrange string expectedUri = $"{ServiceRoot}/Employees(EmpNumber=8,EmpType=Microsoft.OData.Client.Tests.Tracking.EmployeeType'PartTime',OrgId=1)"; @@ -180,13 +192,17 @@ public void UseByKeyToFilterByCompositeKeys_WithEnumAsKey_DoNotThrowException() } ] }"; - SetupContextWithRequestPipeline(new DataServiceContext[] { _defaultContext }, response, "Employees"); + SetupContextWithRequestPipeline(_defaultContext, response, "Employees"); + _defaultContext.SendingRequest2 += (sender, args) => + { + Assert.Equal(expectedUri, args.RequestMessage.Url.ToString()); + }; // Act EmployeeSingle query = _defaultContext.Employees.ByKey( new Dictionary() { { "EmpNumber", 8 }, { "EmpType", EmployeeType.PartTime }, { "OrgId", 1 } }); - Employee employee = query.GetValue(); + Employee employee = await query.GetValueAsync().ConfigureAwait(false); // Assert Assert.Equal(expectedUri, query.Query.ToString()); @@ -194,22 +210,53 @@ public void UseByKeyToFilterByCompositeKeys_WithEnumAsKey_DoNotThrowException() Assert.Equal(EmployeeType.PartTime, employee.EmpType); } - private void SetupContextWithRequestPipeline(DataServiceContext[] contexts, string response, string path) + [Fact] + public void FilterByEnumKey_WithEnumAsKey_DoNotThrowException() { - string location = $"{ServiceRoot}/{path}"; + // Arrange + string expectedUri = $"{ServiceRoot}/Employees(Microsoft.OData.Client.Tests.Tracking.EmployeeType'FullTime')"; - foreach (var context in contexts) + string response = @"{ + ""@odata.context"": ""http://localhost:8007/$metadata#Employees"", + ""value"": [ + { + ""EmpNumber"": 9, + ""EmpType"": ""FullTime"", + ""OrgId"": 1, + ""Name"": ""John Doe"" + } + ] +}"; + SetupContextWithRequestPipeline(_defaultContext, response, "Employees"); + _defaultContext.SendingRequest2 += (sender, args) => { - context.Configurations.RequestPipeline.OnMessageCreating = - (args) => new CustomizedRequestMessage( - args, - response, - new Dictionary() - { - { "Content-Type", "application/json;charset=utf-8" }, - { "Location", location }, - }); - } + Assert.Equal(expectedUri, args.RequestMessage.Url.ToString()); + }; + + // Act + EmployeeSingle query = _defaultContext.Employees.ByKey( + new Dictionary() { { "EmpType", EmployeeType.FullTime } }); + + Employee employee = query.GetValue(); + + // Assert + Assert.Equal(expectedUri, query.Query.ToString()); + Assert.Equal(9, employee.EmpNumber); + Assert.Equal(EmployeeType.FullTime, employee.EmpType); + } + + private void SetupContextWithRequestPipeline(DataServiceContext context, string response, string path) + { + string location = $"{ServiceRoot}/{path}"; + + context.Configurations.RequestPipeline.OnMessageCreating = (args) => new CustomizedRequestMessage( + args, + response, + new Dictionary() + { + { "Content-Type", "application/json;charset=utf-8" }, + { "Location", location }, + }); } class Container : DataServiceContext