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
$apply is not showing meta data info when I use IActionResult #181
Comments
And in this first case, " $count=true " has no effect. |
In use:
I am pretty happy to be able to use §apply and groupby now, like for example:
Filter, groupBy, orderBy and paging with top and skip actually work. But the result of those queries do not conform to the usual OData-Results and therefor do not contain the count and nextLink for example. A usual OData-Result is returned by queries without Request-Url: "{{baseUrl}}/odata/Article?$select=Id&$expand=Brand($select=Name)&$top=3&$count=true"
Response:
{
"@odata.context": "https://{{baseUrl}}/odata/$metadata#Article(Id,Brand(Name))",
"@odata.count": 13023,
"value": [
{
"Id": 1,
"Brand": {
"Name": "Example1"
}
},
{
"Id": 2,
"Brand": {
"Name": "Example2"
}
},
{
"Id": 3,
"Brand": {
"Name": "Example3"
}
}
]
} When using Request-Url: "{{baseUrl}}/odata/Article?$apply=groupby((Brand/Id))&$top=3&$skip=30&$orderby=Brand/Id&$count=true"
Response:
[
{
"Brand": {
"Id": 1302
}
},
{
"Brand": {
"Id": 1303
}
},
{
"Brand": {
"Id": 1304
}
}
] I did not notice until now, but I am also missing Our actions look like this: [EnableQuery]
public IActionResult Get()
{
var dbContext = HttpContext.RequestServices.GetService<TDbContext>();
var dbSet = dbContext.Set<TEntity>();
return Ok(dbSet);
} |
i'm going to follow this, i have the same problem (missing meta data info) when using IQueryable as controller action result.
used packages: |
Is there any updates here? |
Any progress? |
@komdil and @vonckm-kadaster , a "payload without metadata" is a normal AspNetCore MVC payload, which means your endpoints are not being detected as actual OData endpoints. Can you try changing the signatures and/or using attribute routing? For example, I think the extra Regardless, please use the route debug middleware to make sure your routes are actually OData routes. |
I had the same issue and after debuging I found that the The As solution I created a custom public override void OnActionExecuted(ActionExecutedContext actionExecutedContext)
{
if (actionExecutedContext is null)
{
throw new ArgumentNullException(nameof(actionExecutedContext));
}
if (actionExecutedContext.HttpContext.Response != null &&
IsSuccessStatusCode(actionExecutedContext.HttpContext.Response.StatusCode) &&
actionExecutedContext.Result is ObjectResult content &&
content.Value != null &&
content.DeclaredType == null)
{
// To help the `ODataOutputFormatter` to determine the correct output class
// the `content.DeclaredType` needs to be set before appling the `$apply`-option
// so that a valid `OData`-result is produced
// https://github.com/OData/AspNetCoreOData/blob/4de92f52a346606a447ec4df96c5f3cd05642f50/src/Microsoft.AspNetCore.OData/Formatter/ODataOutputFormatter.cs#L127-L131
content.DeclaredType = content.Value.GetType();
}
base.OnActionExecuted(actionExecutedContext);
}
private static bool IsSuccessStatusCode(int statusCode)
{
return statusCode >= 200 && statusCode < 300;
} |
@cympatic could you share the controller code where you had to use this custom attribute on? |
Not directly. However, it's easily reproducible by using the E2E EntitySetAggregation tests in this repository. The [EnableQuery]
public IQueryable<Customer> Get()
{
return _context.Customers;
} and the result will be: But changing the [EnableQuery]
public IActionResult Get()
{
return Ok(_context.Customers);
} Then the result will be Creating a custom public class MyEnableQueryAttribute : EnableQueryAttribute
{
public override void OnActionExecuted(ActionExecutedContext actionExecutedContext)
{
if (actionExecutedContext is null)
{
throw new ArgumentNullException(nameof(actionExecutedContext));
}
if (actionExecutedContext.HttpContext.Response != null &&
IsSuccessStatusCode(actionExecutedContext.HttpContext.Response.StatusCode) &&
actionExecutedContext.Result is ObjectResult content &&
content.Value != null &&
content.DeclaredType == null)
{
// To help the `ODataOutputFormatter` to determine the correct output class
// the `content.DeclaredType` needs to be set before appling the `$apply`-option
// so that a valid `OData`-result is produced
// https://github.com/OData/AspNetCoreOData/blob/4de92f52a346606a447ec4df96c5f3cd05642f50/src/Microsoft.AspNetCore.OData/Formatter/ODataOutputFormatter.cs#L127-L131
content.DeclaredType = content.Value.GetType();
}
base.OnActionExecuted(actionExecutedContext);
}
private static bool IsSuccessStatusCode(int statusCode)
{
return statusCode >= 200 && statusCode < 300;
}
} and updating the [MyEnableQuery]
public IActionResult Get()
{
return Ok(_context.Customers);
} will result in And the specific test will succeed again A commit pushed to the fork I've: example issue 181. |
Maybe I'm missing something obvious, but that test doesn't work at all for me:
|
That's strange. I just forked this repository, cloned it to my machine, removed the |
OData.Issue.181.zip |
It does. Took me a while to realize how to make this work (for some weird reason...), but I can see now how it's supposed to be handled. Change the method as per the following: [EnableQuery]
[HttpGet("AsActionResult")]
public ActionResult<IQueryable<Customer>> AsActionResult()
{
return _context.Customers;
} The conversion from Using In this scenario, I'd not recommend any changes be made to OData/EnableQueryAttribute at all. |
The change [EnableQuery]
[HttpGet("AsActionResult")]
public ActionResult<IQueryable<Customer>> AsActionResult()
{
return _context.Customers;
} result still in a JSON result and not in an OData result when
Using
|
It definitely works for me:
The test is failing for me with that change. EDIT: @cympatic did you perhaps forget to remove the |
Thanks! That was indeed the problem, I missed that |
@julealgon is it fair to say that this issue is solved? Or at least explained well enough? |
As I mentioned above, from my perspective this appears to be working properly considering how using I'm not part of the OData team however, so I can't close this myself. Either @komdil can validate and close, or we can wait for someone on the team to take a look. |
Prior to closing I would like to encourage updating samples / documentation at minimum to reflect the requirement of using |
@cympatic Thanks for bringing this up and providing a workaround! In my case I had to apply the ODataQueryOptions on my own. The documentation shows this example:
But this code doesn't work if select or apply is used because after applying the query isn't of type
This in turn brings up the problem from this reported issue. I ended up with a simple ActionFilterAttribute that I can attach to these special cases:
@xuzhg Documentation needs to be adjusted and is there a real solution to that problem? |
summary to any newcomer Date: 2024-04-12 Official document example: My Controller Action -- return OkObjectResult
Simple Query: http://localhost:5237/api/people
perfect!
missing @odata.context and @odata.count My Controller Action -- return IQueryable
Query with $apply: http://localhost:5237/api/people?$apply=groupby((name),aggregate($count as count))&$count=true
perfect! The conclusion is: |
I have Get action in controller:
When I send this query: Student?$apply=aggregate($count as OrderCount)
It is returning value without meta data.
Actual:
Expected:
It is working fine when Get looks like:
It is causing problems when entity is Open Type. In open type it looks like:
My project is in .NET 5
The text was updated successfully, but these errors were encountered: