'OData Expand Child of Child Missing Key Exception

AspNetCore 3.1 web app with OData version 7.5.2 installed.

I am getting an exception when I do a child of child expand:

i.e.

https://localhost:5001/clinic?$expand=clients($expand=allergies)

If I just expand on clients with clinic - it works fine

https://localhost:5001/clinic?$expand=clients

or clients expanded with allergies - it works.

https://localhost:5001/client?$expand=allergies

The second level of expansion is throwing the error.

enter image description here

I have entities defined:

public class Clinic 
{
    public Guid Id { get; set; }
    public string Name { get; set; }

    public string Street { get; set; }
    public string Unit { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zipcode { get; set; }
    public string PhoneNumber { get; set; }
    public string PhoneDescription { get; set; }
    public DateTime Created { get; set; }
    public DateTime Modified { get; set; }
    public Guid ApplicationId { get; set; }

    public virtual Application Application { get; set; }
    public virtual IEnumerable<Client> Clients { get; set; }
}

public class Client
{
    public Guid Id { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
    public string MiddleName { get; set; }
    public string Street { get; set; }
    public string Unit { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zipcode { get; set; }
    public DateTime DateOfBirth { get; set; }
    public ClientStatus Status { get; set; }
    public DateTime Created { get; set; }
    public DateTime Modified { get; set; }
    public Guid ClinicId { get; set; }

    public virtual Clinic Clinic { get; set; }
    public virtual ICollection<ClientAllergy> Allergies { get; set; }
}

public class ClientAllergy 
{
    public Guid Id { get; set; }
    public string Allergy { get; set; }
    public string AdditionalInformation { get; set; }
    public string Reaction { get; set; }
    public DateTime Created { get; set; }
    public DateTime Modified { get; set; }
    public Guid ClientId { get; set; }

    public virtual Client Client { get; set; }
}

The EdmModel is built:

        app.UseEndpoints(endpoints =>
        {
            endpoints.Expand().Select().OrderBy().Filter();
            endpoints.EnableDependencyInjection();
            endpoints.MapODataRoute("odata", "", GetEdmModel());
        });
    }
    
    private static IEdmModel GetEdmModel()
    {
        var builder = new ODataConventionModelBuilder();
        builder.EntitySet<Clinic>("Clinics");
        builder.EntitySet<Client>("Clients");
        builder.EntitySet<Application>("Applications");
        builder.EntitySet<ClientAllergy>("ClientAllergies");
        return builder.GetEdmModel();
    }

And the $metadata page looks to be correctly registering the entities.

<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:DataServices>
    <Schema Namespace="ABC.Data.Entity" xmlns="http://docs.oasis-open.org/odata/ns/edm">
        <EntityType Name="Clinic">
            <Key>
                <PropertyRef Name="Id" />
            </Key>
            <Property Name="Id" Type="Edm.Guid" Nullable="false" />
            <Property Name="Name" Type="Edm.String" />
            <Property Name="Street" Type="Edm.String" />
            <Property Name="Unit" Type="Edm.String" />
            <Property Name="City" Type="Edm.String" />
            <Property Name="State" Type="Edm.String" />
            <Property Name="Zipcode" Type="Edm.String" />
            <Property Name="PhoneNumber" Type="Edm.String" />
            <Property Name="PhoneDescription" Type="Edm.String" />
            <Property Name="Created" Type="Edm.DateTimeOffset" Nullable="false" />
            <Property Name="Modified" Type="Edm.DateTimeOffset" Nullable="false" />
            <Property Name="ApplicationId" Type="Edm.Guid" />
            <NavigationProperty Name="Application" Type="ABC.Data.Entity.Application">
                <ReferentialConstraint Property="ApplicationId" ReferencedProperty="Id" />
            </NavigationProperty>
            <NavigationProperty Name="Clients" Type="Collection(ABC.Data.Entity.Client)" />
        </EntityType>
        <EntityType Name="Client">
            <Key>
                <PropertyRef Name="Id" />
            </Key>
            <Property Name="Id" Type="Edm.Guid" Nullable="false" />
            <Property Name="LastName" Type="Edm.String" />
            <Property Name="FirstName" Type="Edm.String" />
            <Property Name="MiddleName" Type="Edm.String" />
            <Property Name="Street" Type="Edm.String" />
            <Property Name="Unit" Type="Edm.String" />
            <Property Name="City" Type="Edm.String" />
            <Property Name="State" Type="Edm.String" />
            <Property Name="Zipcode" Type="Edm.String" />
            <Property Name="DateOfBirth" Type="Edm.DateTimeOffset" Nullable="false" />
            <Property Name="Status" Type="ABC.Data.Entity.ClientStatus" Nullable="false" />
            <Property Name="Created" Type="Edm.DateTimeOffset" Nullable="false" />
            <Property Name="Modified" Type="Edm.DateTimeOffset" Nullable="false" />
            <Property Name="ClinicId" Type="Edm.Guid" />
            <NavigationProperty Name="Clinic" Type="ABC.Data.Entity.Clinic">
                <ReferentialConstraint Property="ClinicId" ReferencedProperty="Id" />
            </NavigationProperty>
            <NavigationProperty Name="Allergies" Type="Collection(ABC.Data.Entity.ClientAllergy)" />
        </EntityType>
        <EntityType Name="Application">
            <Key>
                <PropertyRef Name="Id" />
            </Key>
            <Property Name="Id" Type="Edm.Guid" Nullable="false" />
            <Property Name="Name" Type="Edm.String" />
            <Property Name="Created" Type="Edm.DateTimeOffset" Nullable="false" />
            <Property Name="Modified" Type="Edm.DateTimeOffset" Nullable="false" />
            <NavigationProperty Name="Partners" Type="Collection(ABC.Data.Entity.Clinic)" />
        </EntityType>
        <EntityType Name="ClientAllergy">
            <Key>
                <PropertyRef Name="Id" />
            </Key>
            <Property Name="Id" Type="Edm.Guid" Nullable="false" />
            <Property Name="Allergy" Type="Edm.String" />
            <Property Name="AdditionalInformation" Type="Edm.String" />
            <Property Name="Reaction" Type="Edm.String" />
            <Property Name="Created" Type="Edm.DateTimeOffset" Nullable="false" />
            <Property Name="Modified" Type="Edm.DateTimeOffset" Nullable="false" />
            <Property Name="ClientId" Type="Edm.Guid" />
            <NavigationProperty Name="Client" Type="ABC.Data.Entity.Client">
                <ReferentialConstraint Property="ClientId" ReferencedProperty="Id" />
            </NavigationProperty>
        </EntityType>
        <EnumType Name="ClientStatus">
            <Member Name="Active" Value="0" />
            <Member Name="Deceased" Value="1" />
            <Member Name="DMC" Value="2" />
            <Member Name="NotAssess" Value="3" />
            <Member Name="OnHold" Value="4" />
        </EnumType>
    </Schema>
    <Schema Namespace="Default" xmlns="http://docs.oasis-open.org/odata/ns/edm">
        <EntityContainer Name="Container">
            <EntitySet Name="Clinics" EntityType="ABC.Data.Entity.Clinic">
                <NavigationPropertyBinding Path="Application" Target="Applications" />
                <NavigationPropertyBinding Path="Clients" Target="Clients" />
            </EntitySet>
            <EntitySet Name="Clients" EntityType="ABC.Data.Entity.Client">
                <NavigationPropertyBinding Path="Allergies" Target="ClientAllergies" />
                <NavigationPropertyBinding Path="Clinic" Target="Clinics" />
            </EntitySet>
            <EntitySet Name="Applications" EntityType="ABC.Data.Entity.Application">
                <NavigationPropertyBinding Path="Partners" Target="Clinics" />
            </EntitySet>
            <EntitySet Name="ClientAllergies" EntityType="ABC.Data.Entity.ClientAllergy">
                <NavigationPropertyBinding Path="Client" Target="Clients" />
            </EntitySet>
        </EntityContainer>
    </Schema>
</edmx:DataServices>
</edmx:Edmx>


Solution 1:[1]

This is a Data related issue, your schema looks correct. The giveaway here is that the error is raised when the objects are being materialized from the data provider into the OData Model, there are many more steps in the stack trace that would be expected if this occurred later in serialization.

Query the same allergies from the database using EF in a unit test or directly in your data store (assuming SQL). If you see any with a NULL value for the Id, then you should fix or remove those records.

A further proof is to try this query:

https://localhost:5001/clinic?$expand=clients($expand=allergies;$filter=Id ne null)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Chris Schaller