Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class ODataUriResolver
/// <remarks>
/// All extensions should look at this property and keep case sensitive behavior consistent.
/// </remarks>
public virtual bool EnableCaseInsensitive { get; set; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From your notes in the PR, it says that the case insensitive for the query options key name no matter whether it prefixes with '$', Is it right?

Here, if we default to 'true', it means all the Uri parts are case insensitive by default?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xuzhg That statement is directly from the standard: OData V4.01 states that services MUST support case-insensitive system query option names specified with or without the $ prefix.

With this change, the URI parts are case-insensitive by default - which is what the UnqualifiedODataUriResolver does in ASP.NET Core OData here:
https://github.com/OData/AspNetCoreOData/blob/1368836752963d88e920d9508d94807eb995894b/src/Microsoft.AspNetCore.OData/ODataOptions.cs#L326

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @gathogojr,

Could you clarify why this MR changes the handling of property names? The standard specifies that query parameter names (like $expand) should be case-insensitive, but not their values. For example, $exand and $EXPand should be treated as equivalent, but $expand=shoe and $expand=Shoe should not.

This has been a pain point for me regarding case insensitivity—especially since C# class properties are also resolved in a case-insensitive manner, though I haven’t found a clear justification for it. (Related PR where I struggled to write tests)

Could you explain why property names should be handled case-insensitively?

public virtual bool EnableCaseInsensitive { get; set; } = true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought this was already true by default.

Could you add a test that makes this to false.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@WanjohiSammy The default was false, as you can see from the following tests:
image

For the UnqualifiedODataUriResolver in ASP.NET Core OData here
https://github.com/OData/AspNetCoreOData/blob/1368836752963d88e920d9508d94807eb995894b/src/Microsoft.AspNetCore.OData/ODataOptions.cs#L326, we set it to true to support case insensitive behaviour when resolving OData URIs.

Regarding your ask, do you need me to add a test like the following:

        [Fact]
        public void TestEnableCaseInsensitiveBuiltinIdentifierSetToFalse()
        {
            Assert.False((new ODataUriResolver { EnableCaseInsensitive = false }).EnableCaseInsensitive);
        }

Copy link
Member

@WanjohiSammy WanjohiSammy Jun 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this will suffice


/// <summary>
/// Gets and sets the optional-$-sign-prefix for OData system query option.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,13 @@ public void BindPropertyShouldReturnCorrectPropertyIfFoundForComplex()
Assert.Same(HardCodedTestModel.GetAddressMyNeighborsProperty(), result);
}

[Fact]
public void BindPropertyShouldBeCaseSensitive()
[Theory]
[InlineData("Shoe")]
[InlineData("shoe")]
public void BindPropertyShouldNotBeCaseSensitive(string propertyName)
{
var result = InnerPathTokenBinder.BindProperty(HardCodedTestModel.GetPersonTypeReference(), "shoe", DefaultUriResolver);
Assert.Null(result);
var result = InnerPathTokenBinder.BindProperty(HardCodedTestModel.GetPersonTypeReference(), propertyName, DefaultUriResolver);
Assert.Same(HardCodedTestModel.GetPersonShoeProp(), result);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ namespace Microsoft.OData.Tests.UriParser.Metadata
public class ODataUriResolverTests : ExtensionTestBase
{
[Fact]
public void DefaultEnableCaseInsensitiveShouldbeFalse()
public void DefaultEnableCaseInsensitiveShouldBeTrue()
{
ODataQueryOptionParser parser2 = new ODataQueryOptionParser(HardCodedTestModel.TestModel, null, null, new Dictionary<string, string>()) { Resolver = new ODataUriResolver() };
Assert.False(parser2.Resolver.EnableCaseInsensitive);
Assert.True(parser2.Resolver.EnableCaseInsensitive);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,9 +343,15 @@ public void DefaultEnableTemplateParsingShouldBeFalse()
}

[Fact]
public void DefaultEnableCaseInsensitiveBuiltinIdentifierShouldBeFalse()
public void DefaultEnableCaseInsensitiveBuiltinIdentifierShouldBeTrue()
{
Assert.False(new ODataUriResolver().EnableCaseInsensitive);
Assert.True(new ODataUriResolver().EnableCaseInsensitive);
}

[Fact]
public void TestEnableCaseInsensitiveSetToFalse()
{
Assert.False((new ODataUriResolver { EnableCaseInsensitive = false }).EnableCaseInsensitive);
}

[Fact]
Expand Down