AutoMapper / AutoMapper.Extensions.OData

Creates LINQ expressions from ODataQueryOptions and executes the query.
MIT License
140 stars 38 forks source link

Count #111

Closed Robelind closed 2 years ago

Robelind commented 2 years ago

I'm having problems retrieving the count for an entity type.

Controller:

[HttpGet]
public Task<IQueryable<Item>> Get(ODataQueryOptions<Item> options)
{
    return(_dbContext.Items.GetQueryAsync(_mapper, options, null));
}

Request:

GET http://localhost:56168/api/Items/$count HTTP/1.1
Host: localhost:56168
OData-Version: 4.0
OData-MaxVersion: 4.0
Accept: text/plain
Accept-Charset: UTF-8
User-Agent: Microsoft.OData.Client/7.9.2

Result:

Microsoft.OData.ODataException: The value of type 'System.Collections.Generic.List`1[[CompactStore.Integration.Item, CompactStore.Integration, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' could not be converted to a raw string.
   at Microsoft.OData.RawValueWriter.WriteRawValue(Object value)
   at Microsoft.OData.ODataRawOutputContext.WriteValueImplementation(Object value)
   at Microsoft.OData.ODataRawOutputContext.<>c__DisplayClass18_0.<WriteValueAsync>b__0()
   at Microsoft.OData.TaskUtils.GetTaskForSynchronousOperationReturningTask(Func`1 synchronousOperation)
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.OData.Formatter.Serialization.ODataRawValueSerializer.WriteObjectAsync(Object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext)
   at Microsoft.AspNetCore.OData.Formatter.ODataOutputFormatterHelper.WriteToStreamAsync(Type type, Object value, IEdmModel model, ODataVersion version, Uri baseAddress, MediaTypeHeaderValue contentType, HttpRequest request, IHeaderDictionary requestHeaders, IODataSerializerProvider serializerProvider)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor.ExecuteAsyncEnumerable(ActionContext context, ObjectResult result, Object asyncEnumerable, Func`2 reader)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeResultFilters>g__Awaited|27_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_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.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)

Changing the controller implementation to this works as expected:

[HttpGet]
[EnableQuery]
public IQueryable<Item> Get()
{
    return (_mapper.ProjectTo<Item>(_dbContext.Items));
}
BlaiseD commented 2 years ago

$count=true should work.

I don't think $count by itself is exposed by ODataQueryOptions.Count otherwise a PR would be welcome.

Robelind commented 2 years ago

http://localhost:56168/api/Items?$count-true returns all items.

http://localhost:56168/api/Items?$count=true returns all items and the count.

BlaiseD commented 2 years ago

Correct. "$count-true" was a typo - meant to say "$count=true" like in the link.

Robelind commented 2 years ago

Thought as much, but still doesn't solve the problem. You want only the count, not the items, which http://localhost:56168/api/Items/$count accomplishes with the controller implementation not using the OData extension.

BlaiseD commented 2 years ago

If you're seeing a solution then it's ok to create a PR.