Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to add a dynamic property to my OData response which lives outside the Entity Type and is not included in the EDM Model? #2854

Open
sihanook opened this issue Aug 31, 2024 · 6 comments
Assignees

Comments

@sihanook
Copy link

sihanook commented Aug 31, 2024

I have a generically typed OData controller that is able to handle requests from multiple routes/entities. This is hooked up using custom IApplicationFeatureProvider<ControllerFeature> and IControllerModelConvention implementations, similar to the way it is described in the blog (in Approach 2).

My EDM model has 2 entities: one and two. They have different properties. I have this setup to access these entity sets by using routes like:

  • /api/one
  • /api/two
  • /api/one?$filter=id eq 2&$select=id,name&$count=true

The current API response looks some like below:

{
  "@odata.context": "http://localhost/api/$metadata#one(Id,Name)",
  "@odata.count": 1,
  "value": [
    {
      "Id": "2",
      "Name": "My name 2"
    }
  ]
}

The code in this above state is available at the location here: https://github.com/sihanook/dynamicodataroutingsample

This all works fine except that now I want to add an extra property with some additional metadata about the result set to the response. So, I want the response from the API to look like below, but still be able to use the routes as above:

{
  "@odata.context": "http://localhost/api/$metadata#one(Id,Name)",
  "@odata.count": 1,
  "value": {
    "content": [
      {
        "Id": "2",
        "Name": "My name 2"
      }
    ],
    "additionalmetadata": {
      "someProperty1": "someValue 1",
      "someProperty2": "someValue 2"
    }
  }
}

What is the correct way to achieve this in .NET Core OData? I am using version 9.0.0.

Assemblies affected

I am using "Microsoft.AspNetCore.OData" version "9.0.0".

@sihanook
Copy link
Author

sihanook commented Aug 31, 2024

I tried the approach in this PR: sihanook/dynamicodataroutingsample#3

Note how with this approach, the response no longer looks like an OData response and "odata.count" is missing, although the rest of the response looks like what I want:

/api/one?$filter=id eq 2&$select=id,name&$count=true

{
  "content": [
    {
      "Id": 2,
      "Name": "B"
    }
  ],
  "metadata": [
    "One",
    "Two"
  ]
}

@xuzhg
Copy link
Member

xuzhg commented Sep 3, 2024

We triaged that this requirement is not compliant to the OData spec. Please follow up the OData spec if you want to build the services to server more clients.

BTW, for the expected payload, do you accept to achieve it using OData instance annotation?

@mikepizzo
Copy link
Member

mikepizzo commented Sep 3, 2024

Note that the format that you propose is not OData compliant, which is why it's hard to generate/support from the OData libraries.

The OData conformant way to add additional metadata to the result set is similar to how odata-defined metadata (like context and count) is added to the payload, which is through annotations.

Representing custom metadata about the result set in an OData compliant manner would look something like:

{
  "@odata.context": "http://localhost/api/$metadata#one(Id,Name)",
  "@odata.count": 1,
  "@my.additionalmetadata": {
      "someProperty1": "someValue 1",
      "someProperty2": "someValue 2"
   },
  "value":  [
    {
      "Id": "2",
      "Name": "My name 2"
    }
  ]
}

Because this is using OData-defined annotation mechanisms for adding metadata to the response it is easier to represent and support through the OData libraries.

Is there a reason that using this annotation mechanism doesn't work for your scenario?

@sihanook
Copy link
Author

sihanook commented Sep 3, 2024

Ah I think the annotations would also work for me. @mikepizzo what is the correct way to add custom annotations in the .NET Core OData library? It looks like I could do it with a custom serializer that overrides the CreateResource method. Is that the best way to do it?

Thanks!

@xuzhg
Copy link
Member

xuzhg commented Sep 3, 2024

Ah I think the annotations would also work for me. @mikepizzo what is the correct way to add custom annotations in the .NET Core OData library? It looks like I could do it with a custom serializer that overrides the CreateResource method. Is that the best way to do it?

Thanks!

I have a PR to support instance annotation container at C# model class. It's still in review and before it's avaiable, yes, you can customize the serializer to achieve it.

@sihanook
Copy link
Author

sihanook commented Sep 3, 2024

@xuzhg if you have any examples of achieving this through a custom serializer, please share it. From a quick look, the ODataResourceSerializer seems to support this at a per-entity level, and not at the aggregate level, but I might be missing something. I'll dig more though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants