Customized Routing Convention can't work with [FromODataUri] due to the accessibility of ODataParameterValue

Open
#300 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
35/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Stale
Tech stack
csharp
Domain
api, backend

Research direction

Start with RoutingConventionHelpers.AddKeyValues and the accessibility of ODataParameterValue, then trace ODataModelBinderConverter.ConvertTo and ODataModelBinder.BindModelAsync for the string-key case. Reproduce the custom routing convention with [FromODataUri] and determine whether the documented or exposed route-value mechanism prevents the key from becoming null; add a regression test for the chosen API behavior.

Written by the indexing model from the issue text.

Description

I have customized routing convention. For the odatapath template like "~/entityset/key", we must extract the value and put it into RoutingConventionsStore or RouteData.Values.
From the odata source code RoutingConventionHelpers.AddKeyValues, it has below code to support without/with FromODataUri case.

// for without FromODataUri
routeValues[name] = routeValue;

// For FromODataUri
string prefixName = ODataParameterValue.ParameterValuePrefix + name;
odataValues[prefixName] = odataValue;

But the ODataParameterValue is an internal class where we cannot leverage it from my customized routing convention. So, my customized routing convention just add the code for "without" FromODataUri.

In this case, if I still use FromODataUri, in some case, the key will be null.
For example, if the type of the key is String which defined in EDM, and my input is "48d31887-5fad-4d73-a9f5-3c356e68a038", for below code, the key will be null, but if the key type is Guid, then everything works.

[EnableQuery]
public IActionResult Get([FromODataUri] string key)
{
    return Ok(_context.Customers.FirstOrDefault(c => c.Id == key));
}

The reason is due to the below code in ODataModelBinderConverter.ConvertTo which is leverage by ODataModelBinder.BindModelAsync. Because the input is "48d31887-5fad-4d73-a9f5-3c356e68a038" which be interpreted as Edm.Guid, and it will fail when you trying to use System.Convert.ChangeType to convert it from Guid to String, the exception is catched by OData lib, so we still can land on the controller/action.

object value;
try
{
    value = ODataUriUtils.ConvertFromUriLiteral(valueString, ODataVersion.V4);
}
catch
{
    if (type == typeof(string))
    {
        return valueString;
    }

    throw;
}

bool isNonStandardEdmPrimitive;
EdmLibHelpers.IsNonstandardEdmPrimitive(type, out isNonStandardEdmPrimitive);

if (isNonStandardEdmPrimitive)
{
    return EdmPrimitiveHelpers.ConvertPrimitiveValue(value, type);
}
else
{
    type = Nullable.GetUnderlyingType(type) ?? type;
    return System.Convert.ChangeType(value, type, CultureInfo.InvariantCulture);
}

Question:
Is there guidlines for customized routing convention to add key value? Should we make ODataParameterValue and relevant classes as public so customized routing convention can use it to solve the problem?

Dominant language
C#
Stars
505
Forks
186
PR merge metrics
No merged PRs in 30d

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from OData/AspNetCoreOData

All issues in OData/AspNetCoreOData

Similar issues

More C# issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.