AspNetCoreOData
AspNetCoreOData copied to clipboard
Derived Class Does Not Show odata.type
Short summary (3-5 sentences) describing the issue.
I have an entity that has a property, and that property is a complex type. When I return a derived class from the abstract complex type the @odata.type is not returned. This causes issues when I am trying to use graph.microsoft.com because graph can't determine it is the derived class.
Assemblies affected
Which assemblies and versions are known to be affected e.g. OData .Net lib 7.x .NET 7 Microsoft.AspNetCore.OData v8.2.0 Microsoft.OData.Core v 7.16
Reproduce steps
Use repo https://github.com/OData/odata.net/files/13048351/WebApplication3.zip (from OData/odata.net#2774)
Run it and send request: http://localhost:5197/odata/IdentityGovernance/PermissionsAnalytics/Findings/%7Bkey%7D/WebApplication3.Models.ExternallyFinding/AccountsWithAccess
Expected result
{
"@odata.context": "http://localhost:5197/odata/$metadata#identityGovernance/PermissionsAnalytics/Findings('key')/WebApplication3.Models.ExternallyFinding/AccountsWithAccess(WebApplication3.Models.EnumeratedAccountsWithAccess/Accounts())",
"@odata.type": "#WebApplication3.Models.EnumeratedAccountsWithAccess",
"Accounts": [
{
"@odata.type": "#WebApplication3.Models.AwsAuthorizationSystem",
"AuthorizationSystemId": "123",
"AuthorizationSystemName": null,
"AuthorizationSystemType": null,
"AwsTitle": null
},
{
"@odata.type": "#WebApplication3.Models.AwsAuthorizationSystem",
"AuthorizationSystemId": "456",
"AuthorizationSystemName": null,
"AuthorizationSystemType": null,
"AwsTitle": null
}
]
}
Actual result
{
"@odata.context": "http://localhost:5197/odata/$metadata#identityGovernance/PermissionsAnalytics/Findings('key')/WebApplication3.Models.ExternallyFinding/AccountsWithAccess(WebApplication3.Models.EnumeratedAccountsWithAccess/Accounts())",
"Accounts": [
{
"@odata.type": "#WebApplication3.Models.AwsAuthorizationSystem",
"AuthorizationSystemId": "123",
"AuthorizationSystemName": null,
"AuthorizationSystemType": null,
"AwsTitle": null
},
{
"@odata.type": "#WebApplication3.Models.AwsAuthorizationSystem",
"AuthorizationSystemId": "456",
"AuthorizationSystemName": null,
"AuthorizationSystemType": null,
"AwsTitle": null
}
]
}
Additional detail
Optional, details of the root cause if known. Delete this section if you have no additional details to add.
@EvanHennisAtMicrosoft
That's by design from the code at: https://github.com/OData/AspNetCoreOData/blob/main/src/Microsoft.AspNetCore.OData/Formatter/Serialization/ODataResourceSerializer.cs#L1756
internal static bool ShouldAddTypeNameAnnotationForComplex(ODataMetadataLevel metadataLevel)
{
switch (metadataLevel)
{
// For complex types, the default behavior matches the requirements for minimal metadata mode, so no
// annotation is necessary.
case ODataMetadataLevel.Minimal:
return false;
// In other cases, this class must control the type name serialization behavior.
case ODataMetadataLevel.Full:
case ODataMetadataLevel.None:
default: // All values already specified; just keeping the compiler happy.
return true;
}
}
Actually, I don't think the first comment for ODataMetadataLevel.Minimal makes sense. @mikepizzo can you share your thoughts?
as a workaround, @EvanHennisAtMicrosoft you can create a class derive from ODataResourceSerializer, override CreateResource, call var resource = base.CreateResource(...); first, then specify the TypeAnnotation for the resource as resource.TypeAnnotation = new ODataTypeAnnotation(typeName);
Let me know the result. Thanks.
As Sam's code shows, this issue is in webapi, not in ODL, so I'm going to move it there.
If WebAPI just correctly sets the typename we should get the expected behavior. I'm not sure why we're trying to override it in WebAPI. The above code in WebAPI looks complicated and wrong.
Code in ODL in minimal metadata to determine if type annotation should be written:
internal override string GetResourceTypeNameForWriting(string expectedTypeName, ODataResourceBase resource, bool isUndeclared = false)
{
Debug.Assert(resource != null, "resource != null");
if (resource.TypeAnnotation != null)
{
return resource.TypeAnnotation.TypeName;
}
// We only write entity type names in Json Light if it's more derived (different) from the expected type name.
string resourceTypeName = resource.TypeName;
if (expectedTypeName != resourceTypeName || isUndeclared)
{
return resourceTypeName;
}
return null;
}
@xuzhg Adding in the code that you suggested allowed this to work.