Open audacity76 opened 7 months ago
@audacity76 did you also observe this issue when using unbound functions?
@habbes In my case it was a bound function. It happened when using the aggregate function on an endpoint
We've confirmed that the current output does not match what's expected and that the response should be a standard OData response. We'll investigate and work on a fix.
I can confirm that is still present with AspNetCoreOData Version 8.2.5, using $apply=aggregate but also $apply=groupby
+1, from a quick look at the code I see that there is no serializer being selected because here (I guess) https://github.com/OData/AspNetCoreOData/blob/599dc1bdca9edc2cf88935ed54fe46ad5478da5b/src/Microsoft.AspNetCore.OData/Edm/DefaultODataTypeMapper.cs#L200
the type IEnumerable<GroupByWrapper>
cannot get a corresponding IEdmType
, thus no serializer is being selected.
Quick fix:
public class CustomODataSerializerProvider : ODataSerializerProvider {
private readonly IServiceProvider _serviceProvider;
public CustomODataSerializerProvider(IServiceProvider serviceProvider) :
base(serviceProvider) {
_serviceProvider = serviceProvider;
}
public override IODataSerializer GetODataPayloadSerializer(Type type, HttpRequest request) {
// Handle GroupByWrapper
var ienumerable = type.GetGenericBaseType(typeof(IEnumerable<>))?.GetGenericArguments()[0];
while(ienumerable != null) {
// Here you could also instantiate it directly with new GroupByODataResourceSetSerializer(...)
if(ienumerable.FullName == "Microsoft.AspNetCore.OData.Query.Wrapper.GroupByWrapper")
return _serviceProvider.GetRequiredService<GroupByODataResourceSetSerializer>();
if (ienumerable.BaseType != null && ienumerable.BaseType != typeof(object))
ienumerable = ienumerable.BaseType;
else
ienumerable = null;
}
return base.GetODataPayloadSerializer(type, request);
}
}
public class GroupByODataResourceSetSerializer : ODataResourceSetSerializer {
public GroupByODataResourceSetSerializer(IODataSerializerProvider serializerProvider) :
base(serializerProvider) {}
public override async Task WriteObjectAsync(object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext) {
IEdmEntitySetBase entitySet = (writeContext.NavigationSource as IEdmEntitySetBase)!;
// The resource type should be the original collection before GroupBy so we retrieve it from the path
var pathType = writeContext.Path.LastSegment.EdmType.AsElementType();
IEdmTypeReference resourceSetType = new EdmCollectionTypeReference(new EdmCollectionType(pathType is IEdmEntityType ? new EdmEntityTypeReference((IEdmEntityType)pathType, false) : new EdmComplexTypeReference((IEdmComplexType)pathType, false)));
var resourceType = resourceSetType.AsCollection().ElementType().AsStructured();
ODataWriter writer = await messageWriter.CreateODataResourceSetWriterAsync(entitySet, resourceType.StructuredDefinition())
.ConfigureAwait(false);
await WriteObjectInlineAsync(graph, resourceSetType, writer, writeContext)
.ConfigureAwait(false);
}
}
And then while configuring:
services.AddMvc(...)
.AddOData((options, s) => {
...
options.AddRouteComponents("odata", s.GetRequiredService<IEdmModel>(), odataServiceCollection => {
...
odataServiceCollection.AddSingleton<IODataSerializerProvider, CustomODataSerializerProvider>();
odataServiceCollection.AddSingleton<GroupByODataResourceSetSerializer>();
});
})
This also returns the correct @odata.context
.
This old issue still is present in the latest AspNetCoreOData Version 8.2.3: https://github.com/OData/WebApi/issues/1712