AspnetCore Odata 7 Returning class name in context and odata.type unexpectedly

Viewed 13

I am using asp.net core 6 and Microsoft.AspNetCore.OData version 7.5.12, I realize this is an older version of the package but I'm pinned to that currently for reasons out of my control.

My situation is I have a single controller that can return a couple of different kinds of entities. Lets say I have dogs and each dog could have walks, so I have the routes:

/api/v1/dogs
/api/v1/dogs/{dog_id}/walks

the first returns a list of dogs and the second returns a list of walks for a given dog with the id of dog_id

To build my edm model I am doing:

private IEdmModel GetEdmModel()
{
   var builder = new ODataConventionModelBuilder();
   builder.EntitySet<Dog>("dogs");
   builder.EntitySet<Walk>("walks");
   return builder.GetEdmModel();
}

to map my routes I am doing:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
   var edmModel = GetEdmModel()
   app.UseEndpoints((Action<IEndpointRouteBuilder>)(endpoints =>
   {
      endpoints.Select().Filter().Count().OrderBy().Expand().MaxTop(new int?());
      endpoints.MapODataRoute("dogs", "api/v1", edmModel);
      endpoints.MapODataRoute("walks", "api/v1/dogs/{dogId}/", edmModel);
....

On my controllers I have the [EnableQuery] attribute on each of the action methods

[Route("api/v1/dogs")]
public class DogController
...
[HttpGet]
[EnableQuery]
public async Task<IActionResult> GetDogs()
{
    var dogs = await _dogRepo.GetAllAsync(); // Returns Enumerable<Dog>
    return Ok(dogs)
}

[HttpGet("{dogId}/walks")]
[EnableQuery]
public async Task<IActionResult> GetWalksAsync([FromRoute] Guid dogId)
{
    var walks = await _walkRepo.GetForDog(dogId); // Returns Enumerable<Walk>
    return Ok(walks)
}

Both of these endpoints seem to be working, I can pass odata query params like $count, $top and see the actions take effect, however the payloads from these two endpoints differ and I'm not sure why. The request to /api/v1/dogs returns:

{
    "@odata.context": "http://localhost/api/v1/$metadata#dogs",
    "value": [
        {
            "Id": "bc576d83-dc33-4c31-988d-53543f4f01f0",
            "Name": "Dog1"
        },
        {
            "Id": "baec93fa-344c-11ed-a261-0242ac120002",
            "Name": "Dog2"
        }
}

which is fine, however thee request to /api/v1/dogs/{dog_id}/walks returns the fully qualified class name for walk in the @odata.context attribute and also includes a @odata.type property that has the fully qualified class name:

"@odata.context": "http://localhost/api/v1/$metadata#Collection(My.Project.My.Model.Namespace.Walk)",
"value": [
    {
        "@odata.type": "#My.Project.My.Model.Namespace.Walk",
        "Id": "03ebcaf6-f94b-48e6-854b-c156a993fcc9",
        "DogId": "bc576d83-dc33-4c31-988d-53543f4f01f0",
        "WalkTime": "2022-09-14T08:59:57.765216-03:00"
    },

I'm really unsure what I am missing here. I feel like I am configuring these similarly, the fact that I can perform odata operations on both of these endpoints suggests to me these are both configured appropriately. I can't find a reason for this descrepancey and not seeing anything in the documentation, especially for this version of Microsoft.AspNetCore.OData, I feel like I am missing something simple here. I did see the post Why does my OData return data contain "@odata.type"? in which the advice is given to modify the client's request however I don't think that would apply to me because I am using the same client, same headers etc, for each endpoint request and I am getting the differing responses. I would also prefer my API to be consistent.

If anyone could suggest what I'm doing wrong here I'd really appreciate the help. I've been banging my head against this for days. Thanks very much!

0 Answers
Related