Skip to content

Error when using QueryContext for dataloaders for abstract classes #8024

@cenx1

Description

@cenx1

Product

Hot Chocolate

Version

15.0.3

Link to minimal reproduction

https://github.com/cenx1/HC15AbstractInheritanceProblem

Steps to reproduce

Use entities that inherit from an abstract class:

[InterfaceType]
public abstract class FarmAnimal
{
    public int Id { get; set; }
    public int FarmerId { get; set; }
    public virtual Farmer FarmerOwner { get; set; } = default!;
}

public class Cow : FarmAnimal
{
    public double AmountOfMilk { get; set; }
}

public class Pig : FarmAnimal
{
    public double AmountOfMeat { get; set; }
}

public class Cat : FarmAnimal
{
    public int Age { get; set; }
}

public class Farmer
{
    public int Id { get; set; }
    public string Name { get; set; } = default!;

    public virtual ICollection<FarmAnimal> FarmAnimals { get; set; } = [];
}

Use DbContext for the entities:

public class AnimalContext : DbContext
{
    public virtual DbSet<FarmAnimal> FarmAnimals { get; set; }
    public virtual DbSet<Farmer> Farmers { get; set; }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<FarmAnimal>(entity => { entity.UseTpcMappingStrategy(); });
        modelBuilder.Entity<Cow>(entity => { entity.ToTable("Cow"); });
        modelBuilder.Entity<Pig>(entity => { entity.ToTable("Pig"); });
        modelBuilder.Entity<Cat>(entity => { entity.ToTable("Cat"); });
    }
}

Create a dataloader that uses QueryContext that fetches a collection of entities that inherits from an abstract class:

[DataLoader]
public static async Task<Dictionary<int, Page<FarmAnimal>>> GetFarmAnimalsByFarmerIdAsync(
    IReadOnlyList<int> farmerIds,
    QueryContext<FarmAnimal> queryContext,
    PagingArguments pagingArgs,
    AnimalContext context,
    CancellationToken cancellationToken
)
{
    var result = await context.FarmAnimals
        .Where(fa => farmerIds.Contains(fa.FarmerId))
        .OrderBy(fa => fa.Id)
        .With(queryContext) // I believe this is the culprit
        .ToBatchPageAsync(x => x.FarmerId, pagingArgs, cancellationToken);

    return result;
}

Execute a query that queries farm animals on farmer:

query a {
  farmer {
    id
    name
    farmAnimals(pagingArguments: { first: 3 }) {
      id
      __typename
      ... on Cat {
        age
      }
      ... on Cow {
        amountOfMilk
      }
      ... on Pig {
        amountOfMeat
      }
    }
  }
}

What is expected?

The response returns something like:

{
  "data": {
    "farmer": {
      "id": "RmFybWVyOjE=",
      "name": "John Doe",
      "farmAnimals": {
        "edges": [
          {
            "node": {
              "id": 1,
              "__typename": "Cow",
              "amountOfMilk": 10
            }
          },
          {
            "node": {
              "id": 2,
              "__typename": "Pig",
              "amountOfMeat": 5
            }
          },
          {
            "node": {
              "id": 3,
              "__typename": "Cat",
              "age": 3
            }
          }
        ]
      }
    }
  }
}

What is actually happening?

Response:

{
  "errors": [
    {
      "message": "Unexpected Execution Error",
      "locations": [
        {
          "line": 5,
          "column": 5
        }
      ],
      "path": [
        "farmer",
        "farmAnimals"
      ],
      "extensions": {
        "message": "The LINQ expression 'DbSet<FarmAnimal>()\r\n    .Where(f => __farmerIds_0\r\n        .Contains(f.FarmerId))\r\n    .GroupBy(f => (f is Cow) ? (FarmAnimal)(FarmAnimal)new Cow{ \r\n        Id = ((Cow)f).Id, \r\n        AmountOfMilk = ((Cow)f).AmountOfMilk, \r\n        FarmerId = f.FarmerId \r\n    }\r\n     : (f is Pig) ? (FarmAnimal)(FarmAnimal)new Pig{ \r\n        Id = ((Pig)f).Id, \r\n        AmountOfMeat = ((Pig)f).AmountOfMeat, \r\n        FarmerId = f.FarmerId \r\n    }\r\n     : (f is Cat) ? (FarmAnimal)(FarmAnimal)new Cat{ \r\n        Id = ((Cat)f).Id, \r\n        Age = ((Cat)f).Age, \r\n        FarmerId = f.FarmerId \r\n    }\r\n     : null.FarmerId)' 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.",
      }
    }
  ],
  "data": {
    "farmer": {
      "id": "RmFybWVyOjE=",
      "name": "John Doe",
      "farmAnimals": null
    }
  }
}

Relevant log output

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions