Webapi: Still unable to $select complex types after version 7.2.2

Created on 15 Sep 2020  路  8Comments  路  Source: OData/WebApi


Hey again, I seem to be unable to re-open the original issue, so I had to open a new one. This #2281 is still there on nightly Microsoft.AspNet.OData 7.5.0-Nightly202009150040. Both on the sample project I linked in that issue, and in our custom project. The issue can still be reproduced in the same steps provided in that issue.

Assemblies affected

  • Microsoft.AspNet.OData 7.5.0-Nightly202009150040
  • From Microsoft.AspNet.OData 7.2.3 up to and including Microsoft.AspNet.OData 7.4.1 (current newest).

Reproduce steps _(Copied from #2281)_

I found this (slightly outdated) sample project, which has a complex type called "Location", similar to what we have in our project. Using that as a base, to reproduce:

  1. Clone repo
  2. Upgrade nuget packages (that project is on Microsoft.AspNet.OData 7.2.1, which works fine)
  3. Run the solution and try to query Events:
    (1) http://localhost:18384/Events
    (2) http://localhost:18384/Events?$select=occursAt

Here the "OccursAt" field is of the complex type "Location".

Expected result _(Copied from #2281)_

This is what you get on version 7.2.2 and below:
(1) {"@odata.context":"http://localhost:18384/$metadata#Events","value":[{"Id":1,"Description":null,"OccursAt":{"Address":"Address1"}}]}

(2) {"@odata.context":"http://localhost:18384/$metadata#Events(OccursAt)","value":[{"OccursAt":{"Address":"Address1"}}]}

Actual result _(Copied from #2281)_

This is what you get on versions above 7.2.2:
(1) {"@odata.context":"http://localhost:18384/$metadata#Events","value":[{"Id":1,"Description":null,"OccursAt":{"Address":"Address1"}}]}

(2) {"error":{"code":"","message":"Cannot compare elements of type 'Microsoft.OData.Service.Sample.Trippin.Models.Location'. Only primitive types, enumeration types and entity types are supported."}}

fixed follow up question

All 8 comments

@MathiasJor Alright,

1)
https://www.myget.org/feed/webapinetcore/package/nuget/Microsoft.AspNet.OData/7.5.0-Nightly202009142118
should work, it hardly removes the null check.

2)
https://www.myget.org/feed/webapinetcore/package/nuget/Microsoft.AspNet.OData/7.5.0-Nightly202009150728

now works at my side. It comes from this PR https://github.com/OData/WebApi/pull/2285.

image

The previous (Nightly202009150040) nightly can't work because the Restier Trippin service is using this, the old implementation can't process this provider and any other customized provider.

Let me know any problem.

So now it seems to work fine for the sample project, thanks :). We are not using Restier in our project.

However, in our actual project we also have a custom serializer, and this still fails for all of our complex types.

A simplified example of our serializer:
image

Attempting to get the ResourceInstance of the expanded resource fails, but only when selecting a complex type (similar query to the original issue). Selecting any normal entity, or primitive property works as expected.

System.ArgumentException
  HResult=0x80070057
  Message=Objekt av typen Microsoft.AspNet.OData.Query.Expressions.SelectExpandBinder+SelectAll`1[Modulus.OData.Domain.Models.Klassifikation.ODataClassificationValue] kan ikke konverteres til typen Modulus.OData.Domain.Models.Klassifikation.ODataClassificationValue.
  Source=mscorlib
  StackTrace:
   at System.RuntimeType.TryChangeType(Object value, Binder binder, CultureInfo culture, Boolean needsSpecialCast)
   at System.RuntimeType.CheckValue(Object value, Binder binder, CultureInfo culture, BindingFlags invokeAttr)
   at System.Reflection.MethodBase.CheckArguments(Object[] parameters, Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig)
   at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
   at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, Object[] index)
   at Microsoft.AspNet.OData.ResourceContext.BuildResourceInstance()
   at Microsoft.AspNet.OData.ResourceContext.get_ResourceInstance()
   at Modulus.OData.Host.Serialization.ODataMaskingSerializer.CreateStructuralProperty(IEdmStructuralProperty structuralProperty, ResourceContext resourceContext) in D:\Mathias\Jobb\NCCGV0010\Solution\Platform\OData\Modulus.OData.Host\Serialization\ODataMaskingSerializer.cs:line 35
   at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSerializer.CreateStructuralPropertyBag(SelectExpandNode selectExpandNode, ResourceContext resourceContext)
   at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSerializer.CreateResource(SelectExpandNode selectExpandNode, ResourceContext resourceContext)
   at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSerializer.WriteResource(Object graph, ODataWriter writer, ODataSerializerContext writeContext, IEdmTypeReference expectedType)
   at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSerializer.WriteObjectInline(Object graph, IEdmTypeReference expectedType, ODataWriter writer, ODataSerializerContext writeContext)
   at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSerializer.WriteComplexAndExpandedNavigationProperty(IEdmProperty edmProperty, SelectItem selectItem, ResourceContext resourceContext, ODataWriter writer)
   at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSerializer.WriteComplexProperties(SelectExpandNode selectExpandNode, ResourceContext resourceContext, ODataWriter writer)
   at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSerializer.WriteResource(Object graph, ODataWriter writer, ODataSerializerContext writeContext, IEdmTypeReference expectedType)
   at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSerializer.WriteObjectInline(Object graph, IEdmTypeReference expectedType, ODataWriter writer, ODataSerializerContext writeContext)
   at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSetSerializer.WriteResourceSet(IEnumerable enumerable, IEdmTypeReference resourceSetType, ODataWriter writer, ODataSerializerContext writeContext)
   at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSetSerializer.WriteObjectInline(Object graph, IEdmTypeReference expectedType, ODataWriter writer, ODataSerializerContext writeContext)
   at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSetSerializer.WriteObject(Object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext)
   at Microsoft.AspNet.OData.Formatter.ODataOutputFormatterHelper.WriteToStream(Type type, Object value, IEdmModel model, ODataVersion version, Uri baseAddress, MediaTypeHeaderValue contentType, IWebApiUrlHelper internaUrlHelper, IWebApiRequestMessage internalRequest, IWebApiHeaders internalRequestHeaders, Func`2 getODataMessageWrapper, Func`2 getEdmTypeSerializer, Func`2 getODataPayloadSerializer, Func`1 getODataSerializerContext)
   at Microsoft.AspNet.OData.Formatter.ODataMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)

  This exception was originally thrown at this call stack:
    [External Code]
    Modulus.OData.Host.Serialization.ODataMaskingSerializer.CreateStructuralProperty(Microsoft.OData.Edm.IEdmStructuralProperty, Microsoft.AspNet.OData.ResourceContext) in ODataMaskingSerializer.cs
    [External Code]

Additional details specific to our project

Below is an excerpt of our entity model with the complex type "ODataClassificationValue". Selecting type ($select=type) on our ODataActivity entity causes the provided stacktrace. In our serializer we're trying to get the "parent" entity to determine if it inherits from a specific interface (ISensitive), which it does in this example.

    public partial class ODataActivity : Entity, ISensitive
    {
        public ODataClassificationValue Type { get; set; }

        ...

    }

I realize this is slightly vague. I can't upload our entire project unfortunatly. Let me know if I can provide any specific details.

Edit

I found another slightly different anomaly in our serializer. Consider the entity:

    public partial class ODataActivity : Entity, ISensitive
    {
        public ODataClassificationValue Type { get; set; }

        public DateTime EventDate { get; set; }

        ...
    }

DateTime is the standard system.DateTime.

An empty query to Activity works fine: ***/odata/Activity. This returns everything as expected:

{
   "@odata.context":"http://localhost:40688/odata/$metadata#Activity",
   "value":[
      {
         "eventDate":"2020-10-28T08:43:00+01:00",
         "type":{
            "userDefinedKey":"Type22",
            "title":"Klage"
         },
      }
   ]
}
  • A query to select eventDate ***/odata/Activity?$select=eventDate works fine.
  • A query to select type crashes with error mentioned originaly ***/odata/Activity?$select=type.
  • A query to select type AND eventDate crashes with similar, but slightly different error than mentioned originaly ***/odata/Activity?$select=eventDate,type. Fails to instantiate the ResourceInstance for the original ResourceContext (so we no longer need to access the ExpandedResource before we reach an invalid ResourceInstance)
    image
    Here is the full stacktrace from the image above:
System.ArgumentException
  HResult=0x80070057
  Message=Objekt av typen Microsoft.AspNet.OData.Query.Expressions.SelectExpandBinder+SelectAll`1[Modulus.OData.Domain.Models.Klassifikation.ODataClassificationValue] kan ikke konverteres til typen Modulus.OData.Domain.Models.Klassifikation.ODataClassificationValue.
  Source=mscorlib
  StackTrace:
   at System.RuntimeType.TryChangeType(Object value, Binder binder, CultureInfo culture, Boolean needsSpecialCast)
   at System.RuntimeType.CheckValue(Object value, Binder binder, CultureInfo culture, BindingFlags invokeAttr)
   at System.Reflection.MethodBase.CheckArguments(Object[] parameters, Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig)
   at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
   at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, Object[] index)
   at Microsoft.AspNet.OData.ResourceContext.BuildResourceInstance()
   at Microsoft.AspNet.OData.ResourceContext.get_ResourceInstance()
   at Modulus.OData.Host.Serialization.ODataMaskingSerializer.CreateStructuralProperty(IEdmStructuralProperty structuralProperty, ResourceContext resourceContext) in D:\Mathias\Jobb\NCCGV0010\Solution\Platform\OData\Modulus.OData.Host\Serialization\ODataMaskingSerializer.cs:line 32
   at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSerializer.CreateStructuralPropertyBag(SelectExpandNode selectExpandNode, ResourceContext resourceContext)
   at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSerializer.CreateResource(SelectExpandNode selectExpandNode, ResourceContext resourceContext)
   at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSerializer.WriteResource(Object graph, ODataWriter writer, ODataSerializerContext writeContext, IEdmTypeReference expectedType)
   at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSerializer.WriteObjectInline(Object graph, IEdmTypeReference expectedType, ODataWriter writer, ODataSerializerContext writeContext)
   at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSetSerializer.WriteResourceSet(IEnumerable enumerable, IEdmTypeReference resourceSetType, ODataWriter writer, ODataSerializerContext writeContext)
   at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSetSerializer.WriteObjectInline(Object graph, IEdmTypeReference expectedType, ODataWriter writer, ODataSerializerContext writeContext)
   at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSetSerializer.WriteObject(Object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext)
   at Microsoft.AspNet.OData.Formatter.ODataOutputFormatterHelper.WriteToStream(Type type, Object value, IEdmModel model, ODataVersion version, Uri baseAddress, MediaTypeHeaderValue contentType, IWebApiUrlHelper internaUrlHelper, IWebApiRequestMessage internalRequest, IWebApiHeaders internalRequestHeaders, Func`2 getODataMessageWrapper, Func`2 getEdmTypeSerializer, Func`2 getODataPayloadSerializer, Func`1 getODataSerializerContext)
   at Microsoft.AspNet.OData.Formatter.ODataMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)

  This exception was originally thrown at this call stack:
    [External Code]
    Modulus.OData.Host.Serialization.ODataMaskingSerializer.CreateStructuralProperty(Microsoft.OData.Edm.IEdmStructuralProperty, Microsoft.AspNet.OData.ResourceContext) in ODataMaskingSerializer.cs
    [External Code]

Again, please just let me know if I can provide any additional information.

I was able to recreate the serializer issue in the trippin sample. I uploaded the modified version to https://github.com/MathiasJor/ODataComplexTypeIssueSample repository. Try to run these queries:

http://localhost:18384/Events (Should work as expected)
http://localhost:18384/Events?$select=occursAt Throws error at line 51 of CustomODataSerializerProvider. http://localhost:18384/Events?$select=occursAt,testTime Throws error at line 47 of CustomODataSerializerProvider.

@MathiasJor There's some feature gaps that can't create the Resource instance if a select or expand in the query.

I have a quick fix at #2288, Would you please try this nightly Microsoft.AspNet.OData.7.5.0-Nightly202009152040?

It should work and it worked at my side using your github repo.

image

Yes, that seems to work on my end!

Thanks a lot for your quick responses and help :). So we can assume this will be available with the release of 7.5.0? :).

Merged the PR https://github.com/OData/WebApi/pull/2288, it will be in 7.5 RTM

Hi @MathiasJor the fix for this issue introduced another (#2316). We've got another fix in flight (#2320) but I want to ensure it doesn't regress your scenario again - could you validate that Nightly202010092336 still works for you?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kpko picture kpko  路  3Comments

MDzyga picture MDzyga  路  5Comments

abkmr picture abkmr  路  3Comments

VikingsFan picture VikingsFan  路  5Comments

davidmorissette picture davidmorissette  路  3Comments