AspNetCoreOData
AspNetCoreOData copied to clipboard
CosmosSDK with ODataQueryOptions breaks when using $select
Assemblies affected ASP.NET Core OData 8.2.3 and Microsoft.Azure.Cosmos 3.35.4
Describe the bug Using the ODataQueryOptions.ApplyTo Method with the Container.GetItemLinqQueryable<T> Method throws an exception only, and only if the user provides a $select option in the url. Any other option such as $top, $skip, $filter work fine. Only when using $select the exception is thrown.
Reproduce steps
- Clone this repo https://github.com/Supernectar/ODataCosmosDBSelectBug.git
- Run the web app
- Make a GET request to any of these urls:
- https://localhost:7234/customers?$select=id
- https://localhost:7234/customers?$select=orders
- https://localhost:7234/customers?$select=orders/amount
- See the exception being returned
NOTE: You need to have a cosmosDB emulator running with a database named "TestDB" and a container named "test". Ideally this container should contain some sample data:
{
"id": "c1429a3e-146c-4df7-8b9e-7c634b19f1a1",
"name": "John Doe",
"orders": [
{
"id": "f0c26d64-7e11-4ed1-8211-efae44f7f57d",
"amount": 50.25,
"type": 0
},
{
"id": "5b4e0849-6b5c-4f75-9e75-86b48cb58353",
"amount": 75.5,
"type": 1
}
]
}
{
"id": "9e1a50a2-8e21-4f70-8f0a-ebbdac2b5e0f",
"name": "Jane Smith",
"orders": [
{
"id": "7273848c-f662-4e6b-b9ed-cd47d3d5e3fc",
"amount": 30.75,
"type": 1
}
]
}
{
"id": "ecb4e60f-5a82-4f10-aae0-06c3e8b21fe0",
"name": "Bob Johnson",
"orders": []
}
Data Model This is the main class where the exception is being generated:
public class CosmosService : ICosmosService
{
private readonly CosmosClient _cosmosClient;
public CosmosService(CosmosClient cosmosClient)
{
_cosmosClient = cosmosClient;
}
private Container Container
{
get => _cosmosClient.GetContainer("TestDB", "test");
}
public async Task<IEnumerable<Customer>> GetCustomers(ODataQueryOptions<Customer> queryOptions)
{
var customerList = new List<Customer>();
var customerQueryable = Container.GetItemLinqQueryable<Customer>();
ODataQuerySettings querySettings = new();
customerQueryable = queryOptions.ApplyTo(customerQueryable, querySettings) as IOrderedQueryable<Customer>;
using FeedIterator<Customer> setIterator = customerQueryable.ToFeedIterator();
while (setIterator.HasMoreResults)
{
var result = await setIterator.ReadNextAsync();
customerList.AddRange(result.Resource);
}
return customerList;
}
}
You can check the rest of the code more in details in this example repo https://github.com/Supernectar/ODataCosmosDBSelectBug.git
Request/Response When doing a GET request to any of these urls
- https://localhost:7234/customers?$select=id
- https://localhost:7234/customers?$select=orders
- https://localhost:7234/customers?$select=orders/amount This is the response I'm getting
System.ArgumentOutOfRangeException: ToFeedIterator is only supported on Cosmos LINQ query operations (Parameter 'linqQuery')
at Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.ToFeedIterator[T](IQueryable`1 query)
at U4PIM.InvoiceManagementAPI.Services.CosmosService.GetCustomers(ODataQueryOptions`1 queryOptions) in C:\Users\GPATACA\source\repos\WebApplication1\Services\CosmosService.cs:line 33
at WebApplication1.Controllers.CustomersController.Get(ODataQueryOptions`1 queryOptions) in C:\Users\GPATACA\source\repos\WebApplication1\Controllers\CustomersController.cs:line 20
at lambda_method6(Closure , Object )
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
Expected behavior I want to be able to use the $select option properly. Given the example data provided above, if I do a GET request to this url https://localhost:7234/customers?$select=orders/amount I want to receive the following data in the response body:
{
"@odata.context": "https://localhost:7234/$metadata#Customers",
"value": [
{
"Orders": [
{
"Amount": 50.25
},
{
"Amount": 75.5
}
]
},
{
"Orders": [
{
"Amount": 30.75
}
]
},
{
"Orders": []
}
]
}
Is this in any way related to:
- https://github.com/OData/AspNetCoreOData/issues/87
?
This seems to be related to this: https://github.com/OData/AspNetCoreOData/issues/1056 too. There is an open issue in the Cosmos SDK repo https://github.com/Azure/azure-cosmos-dotnet-v3/issues/4096 that we are currently following up with.
Is there a status update on this? This would be very useful to be fixed