OData / AspNetCoreOData

ASP.NET Core OData: A server library built upon ODataLib and ASP.NET Core
Other
453 stars 160 forks source link

odata.metadata=full throws exceptions with $apply #1280

Open Xriuk opened 1 month ago

Xriuk commented 1 month ago

Assemblies affected ASP.NET Core OData 8.2.4

Describe the bug When using the query option $apply with odata.metadata=full the results an exception is thrown.

Reproduce steps Reproduce steps A GET request to:

https://.../ProductIdentifiers?$apply=groupby((ProductCode))

with header:

Accept application/json;odata.metadata=full

Throws:

System.NullReferenceException: Object reference not set to an instance of an object.
14:13:19:700       at Microsoft.OData.UriUtils.UriToString(Uri uri)
14:13:19:700       at Microsoft.OData.Evaluation.ODataConventionalUriBuilder.AppendSegment(Uri baseUri, String segment, Boolean escapeSegment)
14:13:19:700       at Microsoft.OData.Evaluation.ODataConventionalUriBuilder.BuildNavigationLinkUri(Uri baseUri, String navigationPropertyName)
14:13:19:700       at Microsoft.OData.Evaluation.ODataConventionalEntityMetadataBuilder.GetNavigationLinkUri(String navigationPropertyName, Uri navigationLinkUrl, Boolean hasNestedResourceInfoUrl)
14:13:19:700       at Microsoft.OData.ODataNestedResourceInfo.get_Url()
14:13:19:700       at Microsoft.OData.JsonLight.ODataJsonLightResourceSerializer.WriteNavigationLinkMetadataAsync(ODataNestedResourceInfo nestedResourceInfo, IDuplicatePropertyNameChecker duplicatePropertyNameChecker)
14:13:19:700       at Microsoft.OData.JsonLight.ODataJsonLightWriter.<StartNestedResourceInfoWithContentAsync>g__StartNestedResourceInfoWithContentInnerAsync|79_0(ODataNestedResourceInfo innerNestedResourceInfo)
14:13:19:700       at Microsoft.OData.ODataWriterCore.<>c.<<CheckForNestedResourceInfoWithContentAsync>b__204_1>d.MoveNext()
14:13:19:700    --- End of stack trace from previous location ---
14:13:19:700       at Microsoft.OData.ODataWriterCore.InterceptExceptionAsync[TArg0](Func`3 action, TArg0 arg0)
14:13:19:700       at Microsoft.OData.ODataWriterCore.CheckForNestedResourceInfoWithContentAsync(ODataPayloadKind contentPayloadKind, ODataItem contentPayload)
14:13:19:700       at Microsoft.OData.ODataWriterCore.WriteStartResourceImplementationAsync(ODataResource resource)
14:13:19:700       at Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.WriteDynamicTypeResourceAsync(Object graph, ODataWriter writer, IEdmTypeReference expectedType, ODataSerializerContext writeContext)
14:13:19:700       at Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.WriteDynamicTypeResourceAsync(Object graph, ODataWriter writer, IEdmTypeReference expectedType, ODataSerializerContext writeContext)
14:13:19:700       at Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.WriteResourceAsync(Object graph, ODataWriter writer, ODataSerializerContext writeContext, IEdmTypeReference expectedType)
14:13:19:700       at Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.WriteObjectInlineAsync(Object graph, IEdmTypeReference expectedType, ODataWriter writer, ODataSerializerContext writeContext)
14:13:19:700       at Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSetSerializer.WriteResourceSetItemAsync(Object item, IEdmStructuredTypeReference elementType, Boolean isUntypedCollection, IEdmTypeReference resourceSetType, ODataWriter writer, IODataEdmTypeSerializer resourceSerializer, ODataSerializerContext writeContext)
14:13:19:700       at Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSetSerializer.WriteResourceSetAsync(IEnumerable enumerable, IEdmTypeReference resourceSetType, ODataWriter writer, ODataSerializerContext writeContext)
14:13:19:700       at Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSetSerializer.WriteObjectInlineAsync(Object graph, IEdmTypeReference expectedType, ODataWriter writer, ODataSerializerContext writeContext)
14:13:19:700       at Tecnoware.Gestionale.OData.GroupByODataResourceSetSerializer.WriteObjectAsync(Object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext) in C:\DevOps\Tecnoware.Gestionale\Backend\OData\GroupByODataResourceSetSerializer.cs:line 21
14:13:19:700       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)
14:13:19:700       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|30_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
14:13:19:700       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
14:13:19:700       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
14:13:19:700       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
14:13:19:700    --- End of stack trace from previous location ---
14:13:19:700       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
14:13:19:700       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
14:13:19:700       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
14:13:19:700       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
14:13:19:700       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
14:13:19:700       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
14:13:19:700       at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
14:13:19:700       at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass6_1.<<UseMiddlewareInterface>b__1>d.MoveNext()
14:13:19:700    --- End of stack trace from previous location ---
14:13:19:700       at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass6_1.<<UseMiddlewareInterface>b__1>d.MoveNext()
14:13:19:700    --- End of stack trace from previous location ---
14:13:19:700       at Microsoft.AspNetCore.Authorization.Policy.AuthorizationMiddlewareResultHandler.HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult)
14:13:19:700       at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
14:13:19:700       at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
14:13:19:700       at Microsoft.AspNetCore.OData.Routing.ODataRouteDebugMiddleware.Invoke(HttpContext context)
14:13:19:700       at Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.Invoke(HttpContext context)
14:13:19:700       at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.<Invoke>g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)
14:13:19:700       at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.HandleException(HttpContext context, ExceptionDispatchInfo edi)
14:13:19:700       at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.<Invoke>g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)
14:13:19:700       at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
14:13:19:700       at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
14:13:19:700       at Microsoft.AspNetCore.Watch.BrowserRefresh.BrowserRefreshMiddleware.InvokeAsync(HttpContext context)
14:13:19:700       at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT`1.ProcessRequestAsync()

Data Model

public class ProductIdentifier{
    [Key]
    public string ProductCode {get; set;}

    [Key]
    public id OwnerId {get; set;}

    [Key]
    public string OwnerProductCode {get; set;}
}

EDM (CSDL) Model

<EntityType Name="ProductIdentifier">
    <Key>
        <PropertyRef Name="ProductCode" />
        <PropertyRef Name="OwnerId" />
        <PropertyRef Name="OwnerProductCode" />
    </Key>
    <Property Name="ProductCode" Type="Edm.String" Nullable="false" />
    <Property Name="OwnerId" Type="Edm.Int32" Nullable="false" />
    <Property Name="OwnerProductCode" Type="Edm.String" Nullable="false" />
    ...
</EntityType>
...
<EntitySet Name="ProductIdentifiers" EntityType="Models.Products.ProductIdentifier" />

Additional context I guess this has something to do with the computation of annotations "@odata.id" for which the required data is missing. The returned as per specs has the same type as the original entity set, but entities returned there are not original entities but aggregations instead, so any annotation should be null I guess. While without odata.metadata=full the response is generated correctly, but "@odata.id" annotations are present (even if not requested), with the correct "null" value

Xriuk commented 1 month ago

Could be somehow related to #1265 ?