Search code examples
c#odataasp.net-core-webapi

OData v7 & 8 - Should Composite Keys be in Alpha Order or HasKey when key value binding?


Odata seems to want keys in alpha order when using key value binding convention.

The below document suggests it should be in the order defined in HasKeys, as the keys aren't in alpha order. https://learn.microsoft.com/en-us/odata/webapi/key-value-binding

Yet if I download https://github.com/OData/AspNetCoreOData

Change line 19 of the below file to switch FirstName and LastName in the HasKey definition https://github.com/OData/AspNetCoreOData/blob/main/sample/ODataRoutingSample/Models/EdmModelBuilder.cs

Switch the order of the 2 properties in the People Model file: https://github.com/OData/AspNetCoreOData/blob/main/sample/ODataRoutingSample/Models/Person.cs

And change the order of the 2 parameters in line 48 of the below file: https://github.com/OData/AspNetCoreOData/blob/main/sample/ODataRoutingSample/Controllers/PeopleController.cs

The API is still expecting the FirstName first: enter image description here

Is this expected behaviour?

Have I missed a way to determine the order of the keys aside from adding a route template to the HttpGet?

I'd prefer use the convention route (no template in the HttpGet), I'm just not sure if there is a way to control the order of the keys the convention uses.

I'm in the process of upgrading a project to .Net 6, OData 8 and I'm trying to move it to using convention routes rather than template specified routes but the priority is, for the moment, to keep the routes the same.

The linked to project is .Net 5, OData 7 but I've confirmed the same issue with .Net 6 and OData 8.


Solution

  • Here's the implementation in the OData modelbuilder:

    var keys = ((IEnumerable<PropertyConfiguration>)config.Keys)
                                         .Concat(config.EnumKeys)
                                         .OrderBy(p => p.Order)
                                         .ThenBy(p => p.Name)
                                         .Select(p => type.DeclaredProperties.OfType<IEdmStructuralProperty>().First(dp => dp.Name == p.Name));
    

    It orders by the 'Order' property, thenby the property name. So, you can set different value for 'Order' to get the orders that you want.

    1. Set the 'Order' property value directly
    2. use 'ColumnAttribute' attribute on the C# property.

    Updated:

    Be noted, in ODataRoutingSample, FirstName and LastName are added explicitly (by call HasKey fluent API), So, 'ColumnAttribute' will be ignore for explicitly added property.

    So, you should set the order value directly. The following codes should work:

    builder.EntitySet<Person>("People").EntityType.HasKey(c => new { c.FirstName, c.LastName });
    builder.EntityType<Person>().Property(c => c.FirstName).Order = 2;
    builder.EntityType<Person>().Property(c => c.LastName).Order = 1;