OData / OData/AspNetCoreOData

any() functions generates untranslatable query

Open
#552 5 comments 0 reactions 1 assignee View on GitHub

@gathogojr is already working on this.

Since Apr 5, 2022.

bug P2
Dominant language
C#
Stars
505
Forks
186
PR merge metrics
No merged PRs in 30d

Description

Assemblies affected
Microsoft.AspNetCore.OData 8.0.8

Describe the bug
I have the following data structure:

  1. Simple entity with a nav property on a collection (Employee)
  2. Second entity with nav property on a collection of first entities (Speciality) (many-to-many relationship)
  3. Bridge entity to bind them

When i do a query like this "https://localhost:7160/odata/Employees?$expand=Specialities&$filter=Specialities/any()"
I get an excection:

The LINQ expression 'DbSet<Employee>()
    .Where(e => MaterializeCollectionNavigation(
        Navigation: Employee.Specialities,
        subquery: DbSet<EmployeeSpeciality>()
            .Where(e0 => EF.Property<Guid?>(e, "Id") != null && object.Equals(
                objA: (object)EF.Property<Guid?>(e, "Id"), 
                objB: (object)EF.Property<Guid?>(e0, "EmployeeId")))
            .Join(
                inner: DbSet<Speciality>(), 
                outerKeySelector: e0 => EF.Property<Guid?>(e0, "SpecialityId"), 
                innerKeySelector: s => EF.Property<Guid?>(s, "Id"), 
                resultSelector: (e0, s) => new TransparentIdentifier<EmployeeSpeciality, Speciality>(
                    Outer = e0, 
                    Inner = s
                ))
            .Select(ti => ti.Inner)) == null ? null : (bool?)DbSet<EmployeeSpeciality>()
        .Where(e1 => EF.Property<Guid?>(e, "Id") != null && object.Equals(
            objA: (object)EF.Property<Guid?>(e, "Id"), 
            objB: (object)EF.Property<Guid?>(e1, "EmployeeId")))
        .Join(
            inner: DbSet<Speciality>(), 
            outerKeySelector: e1 => EF.Property<Guid?>(e1, "SpecialityId"), 
            innerKeySelector: s0 => EF.Property<Guid?>(s0, "Id"), 
            resultSelector: (e1, s0) => new TransparentIdentifier<EmployeeSpeciality, Speciality>(
                Outer = e1, 
                Inner = s0
            ))
        .Any() == (bool?)True)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

EntityFrameworkCore 6.0.3

Data Model

public class Employee
    {
        [Key]
        public Guid Id { get; set; }
        public string Name { get; set; } = string.Empty;
        public ICollection<Speciality> Specialities { get; set; } = new List<Speciality>();
        public Guid? OrganizationId { get; set; }
        public Organization? Organization { get; set; }
    }

public class Speciality
    {
        [Key]
        public Guid Id { get; set; }
        public string Name { get; set; } = string.Empty;
        public ICollection<Employee> Employees { get; set; } = new List<Employee>();
    }
internal class EmployeeSpeciality
    {
        public Guid EmployeeId { get; set; }
        public Guid SpecialityId { get; set; }
    }

EDM (CSDL) Model

<?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="GENERIS.Modules.Base.Entities" xmlns="http://docs.oasis-open.org/odata/ns/edm">
            <EntityType Name="Speciality">
                <Key>
                    <PropertyRef Name="Id" />
                </Key>
                <Property Name="Id" Type="Edm.Guid" Nullable="false" />
                <Property Name="Name" Type="Edm.String" Nullable="false" />
                <NavigationProperty Name="Employees" Type="Collection(GENERIS.Modules.Base.Entities.Employee)" />
            </EntityType>
            <EntityType Name="Employee">
                <Key>
                    <PropertyRef Name="Id" />
                </Key>
                <Property Name="Id" Type="Edm.Guid" Nullable="false" />
                <Property Name="Name" Type="Edm.String" Nullable="false" />
                <NavigationProperty Name="Specialities" Type="Collection(GENERIS.Modules.Base.Entities.Speciality)" />
                <NavigationProperty Name="Organization" Type="GENERIS.Modules.Base.Entities.Organization" />
            </EntityType>
            <EntityType Name="Location">
                <Key>
                    <PropertyRef Name="Id" />
                </Key>
                <Property Name="Id" Type="Edm.Guid" Nullable="false" />
                <Property Name="Name" Type="Edm.String" Nullable="false" />
                <Property Name="ParentId" Type="Edm.Guid" />
                <Property Name="Type" Type="GENERIS.Modules.Base.Entities.LocationType" Nullable="false" />
                <NavigationProperty Name="Parent" Type="GENERIS.Modules.Base.Entities.Location" />
            </EntityType>
            <EntityType Name="Organization">
                <Key>
                    <PropertyRef Name="Id" />
                </Key>
                <Property Name="Id" Type="Edm.Guid" Nullable="false" />
                <Property Name="Name" Type="Edm.String" Nullable="false" />
            </EntityType>
            <EntityType Name="Process">
                <Key>
                    <PropertyRef Name="ProcessId" />
                </Key>
                <Property Name="ProcessId" Type="Edm.String" Nullable="false" />
                <Property Name="CurrentStep" Type="Edm.String" Nullable="false" />
                <Property Name="Input" Type="GENERIS.Modules.Base.Entities.Resource" Nullable="false" />
                <Property Name="Output" Type="GENERIS.Modules.Base.Entities.Resource" Nullable="false" />
                <Property Name="IsFinished" Type="Edm.Boolean" Nullable="false" />
            </EntityType>
            <ComplexType Name="Resource">
                <Property Name="GetLink" Type="Edm.String" />
                <Property Name="Type" Type="Edm.String" />
            </ComplexType>
            <EnumType Name="LocationType">
                <Member Name="Country" Value="0" />
                <Member Name="Region" Value="1" />
                <Member Name="City" Value="2" />
                <Member Name="Area" Value="3" />
                <Member Name="Street" Value="4" />
                <Member Name="Building" Value="5" />
                <Member Name="Room" Value="6" />
                <Member Name="Place" Value="7" />
            </EnumType>
        </Schema>
        <Schema Namespace="Default" xmlns="http://docs.oasis-open.org/odata/ns/edm">
            <EntityContainer Name="Container">
                <EntitySet Name="Specialities" EntityType="GENERIS.Modules.Base.Entities.Speciality">
                    <NavigationPropertyBinding Path="Employees" Target="Employees" />
                </EntitySet>
                <EntitySet Name="Employees" EntityType="GENERIS.Modules.Base.Entities.Employee">
                    <NavigationPropertyBinding Path="Organization" Target="Organizations" />
                    <NavigationPropertyBinding Path="Specialities" Target="Specialities" />
                </EntitySet>
                <EntitySet Name="Locations" EntityType="GENERIS.Modules.Base.Entities.Location">
                    <NavigationPropertyBinding Path="Parent" Target="Locations" />
                </EntitySet>
                <EntitySet Name="Organizations" EntityType="GENERIS.Modules.Base.Entities.Organization" />
                <EntitySet Name="Processes" EntityType="GENERIS.Modules.Base.Entities.Process" />
            </EntityContainer>
        </Schema>
    </edmx:DataServices>
</edmx:Edmx>

Expected behavior
A LINQ expression generated by lib is translatable

Thats what is see in output

Microsoft.EntityFrameworkCore.Query: Debug: Compiling query expression: 
DbSet<Employee>()
    .Where($it => $it.Specialities == null ? null : (bool?)$it.Specialities
        .Any() == (bool?)True)
    .Select($it => new SelectAllAndExpand<Employee>{ 
        Model = __TypedProperty_0, 
        Instance = $it, 
        UseInstanceForProperties = True, 
        Container = new NamedProperty<IEnumerable<SelectAll<Speciality>>>{ 
            Name = "Specialities", 
            Value = $it == null ? null : $it.Specialities == null ? null : $it == null ? null : $it.Specialities
                .Select($it => new SelectAll<Speciality>{ 
                    Model = __TypedProperty_1, 
                    Instance = $it, 
                    UseInstanceForProperties = True 
                }
                ) 
        }
         
    }
    )

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.