You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have a TPC hierarchy consisting of an abstract Pet entity (with Dog and Cat as subclasses) and a Person entity. The Person has navigation collections to Dogs and Cats. With these collection navigation properties present, attempting to query Pets and .Include(p => p.Owner) on the abstract type throws:
System.InvalidOperationException: The expression 'p.Owner' isinvalid inside an 'Include' operation, since it does not represent a property access: 't => t.MyProperty'. To target navigations declared on derived types, use casting('t =>((Derived)t).MyProperty') orthe 'as' operator('t =>(tasDerived).MyProperty').Collection navigation access can be filtered by composing Where,OrderBy(Descending),ThenBy(Descending),Skip or Take operations.Formore information on including related data,seehttps://go.microsoft.com/fwlink/?LinkID=746393.
at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.PopulateIncludeTree(IncludeTreeNodeincludeTreeNode,Expressionexpression,BooleansetLoaded)
at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.ProcessInclude(NavigationExpansionExpressionsource,Expressionexpression,BooleanthenInclude,BooleansetLoaded)
at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.VisitMethodCall(MethodCallExpressionmethodCallExpression)
at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.VisitMethodCall(MethodCallExpressionmethodCallExpression)
at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.Expand(Expressionquery)
at Microsoft.EntityFrameworkCore.Query.QueryTranslationPreprocessor.Process(Expressionquery)
at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutorExpression[TResult](Expressionquery)
at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expressionquery)
at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expressionquery,Booleanasync)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabasedatabase,Expressionquery,IModelmodel,Booleanasync)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass11_0`1.<ExecuteCore>b__0()
at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](ObjectcacheKey, Func`1compiler)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteCore[TResult](Expressionquery,Booleanasync,CancellationTokencancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expressionquery,CancellationTokencancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expressionexpression,CancellationTokencancellationToken)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfooperatorMethodInfo, IQueryable`1source,Expressionexpression,CancellationTokencancellationToken)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfooperatorMethodInfo, IQueryable`1source,LambdaExpressionexpression,CancellationTokencancellationToken)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.FirstOrDefaultAsync[TSource](IQueryable`1source, Expression`1predicate,CancellationTokencancellationToken)
at EFCoreTpcAbstractInclude.Tests.QueryFromAbstractTests.IncludeWithoutCasting()in C:\Code\EFCoreTpcAbstractInclude\EFCoreTpcAbstractInclude\EFCoreTpcAbstractInclude.Tests\QueryFromAbstractTests.cs:line 114
If I remove the collection navigations (public ICollection<Dog> Dogs { get; set; } = []; and public ICollection<Cat> Cats { get; set; } = [];) from Person, the query with .Include(p => p.Owner) succeeds without error.
namespaceEFCoreTpcAbstractInclude.Entities;publicinterfaceIEntity{publicintId{get;set;}}publicclassPerson:IEntity{publicintId{get;set;}publicstring?Name{get;set;}publicintAge{get;set;}// Removing these makes the issue go away.publicICollection<Dog>Dogs{get;set;}=[];publicICollection<Cat>Cats{get;set;}=[];}publicabstractclassPet:IEntity{publicintId{get;set;}publicintAge{get;set;}publicstring?Name{get;set;}publicintOwnerId{get;set;}publicPersonOwner{get;set;}=null!;}publicclassDog:Pet{publicboolLovesChasingSticks{get;set;}}publicclassCat:Pet{publicboolLovesSleeping{get;set;}}
In the attached examples, I have two tests: IncludeWithCasting that always passes, and IncludeWithoutCasting that fails, but in case the Dogs and Cats collection references are removed from the Person entities (and the Init migration is recreated), it passes. The tests, except for the setup, do: With casting, always passes
The migration is the same in both cases, with and without the ICollection references. However, I noticed differences in the generated model snapshots between the two scenarios (with and without the collection navigations on Person). The snapshot with the collections places the foreign key in the derived Dog/Cat entities, while the version without the collections uses a single HasOne("EFCoreTpcAbstractInclude.Entities.Person", "Owner") reference on the Pet entity. This leads me to believe there might be an issue with how EF Core handles TPC, navigation properties, and inheritance in this scenario.
Here's the model snapshot with the collection reference:
// <auto-generated />usingEFCoreTpcAbstractInclude.DataAccess;usingMicrosoft.EntityFrameworkCore;usingMicrosoft.EntityFrameworkCore.Infrastructure;usingMicrosoft.EntityFrameworkCore.Storage.ValueConversion;usingNpgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespaceEFCoreTpcAbstractInclude.DataAccess.Migrations{[DbContext(typeof(AppDbContext))]partialclassAppDbContextModelSnapshot:ModelSnapshot{protectedoverridevoidBuildModel(ModelBuildermodelBuilder){
#pragma warning disable 612,618modelBuilder.HasDefaultSchema("AnimalsDb").HasAnnotation("ProductVersion","9.0.0").HasAnnotation("Relational:MaxIdentifierLength",63);NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);modelBuilder.HasSequence("PersonSequence");modelBuilder.HasSequence("PetSequence");modelBuilder.Entity("EFCoreTpcAbstractInclude.Entities.Person", b =>{b.Property<int>("Id").ValueGeneratedOnAdd().HasColumnType("integer").HasDefaultValueSql("nextval('\"AnimalsDb\".\"PersonSequence\"')");NpgsqlPropertyBuilderExtensions.UseSequence(b.Property<int>("Id"));b.Property<int>("Age").HasColumnType("integer");b.Property<string>("Name").HasColumnType("text");b.HasKey("Id");b.ToTable("People","AnimalsDb");b.UseTpcMappingStrategy();});modelBuilder.Entity("EFCoreTpcAbstractInclude.Entities.Pet", b =>{b.Property<int>("Id").ValueGeneratedOnAdd().HasColumnType("integer").HasDefaultValueSql("nextval('\"AnimalsDb\".\"PetSequence\"')");NpgsqlPropertyBuilderExtensions.UseSequence(b.Property<int>("Id"));b.Property<int>("Age").HasColumnType("integer");b.Property<string>("Name").HasColumnType("text");b.Property<int>("OwnerId").HasColumnType("integer");b.HasKey("Id");b.ToTable((string)null);b.UseTpcMappingStrategy();});modelBuilder.Entity("EFCoreTpcAbstractInclude.Entities.Cat", b =>{b.HasBaseType("EFCoreTpcAbstractInclude.Entities.Pet");b.Property<bool>("LovesSleeping").HasColumnType("boolean");b.HasIndex("OwnerId");b.ToTable("Cats","AnimalsDb");});modelBuilder.Entity("EFCoreTpcAbstractInclude.Entities.Dog", b =>{b.HasBaseType("EFCoreTpcAbstractInclude.Entities.Pet");b.Property<bool>("LovesChasingSticks").HasColumnType("boolean");b.HasIndex("OwnerId");b.ToTable("Dogs","AnimalsDb");});modelBuilder.Entity("EFCoreTpcAbstractInclude.Entities.Cat", b =>{b.HasOne("EFCoreTpcAbstractInclude.Entities.Person","Owner").WithMany("Cats").HasForeignKey("OwnerId").OnDelete(DeleteBehavior.Cascade).IsRequired();b.Navigation("Owner");});modelBuilder.Entity("EFCoreTpcAbstractInclude.Entities.Dog", b =>{b.HasOne("EFCoreTpcAbstractInclude.Entities.Person","Owner").WithMany("Dogs").HasForeignKey("OwnerId").OnDelete(DeleteBehavior.Cascade).IsRequired();b.Navigation("Owner");});modelBuilder.Entity("EFCoreTpcAbstractInclude.Entities.Person", b =>{b.Navigation("Cats");b.Navigation("Dogs");});
#pragma warning restore 612,618}}}
And here's the model, but without the collection reference:
// <auto-generated />usingEFCoreTpcAbstractInclude.DataAccess;usingMicrosoft.EntityFrameworkCore;usingMicrosoft.EntityFrameworkCore.Infrastructure;usingMicrosoft.EntityFrameworkCore.Storage.ValueConversion;usingNpgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespaceEFCoreTpcAbstractInclude.DataAccess.Migrations{[DbContext(typeof(AppDbContext))]partialclassAppDbContextModelSnapshot:ModelSnapshot{protectedoverridevoidBuildModel(ModelBuildermodelBuilder){
#pragma warning disable 612,618modelBuilder.HasDefaultSchema("AnimalsDb").HasAnnotation("ProductVersion","9.0.0").HasAnnotation("Relational:MaxIdentifierLength",63);NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);modelBuilder.HasSequence("PersonSequence");modelBuilder.HasSequence("PetSequence");modelBuilder.Entity("EFCoreTpcAbstractInclude.Entities.Person", b =>{b.Property<int>("Id").ValueGeneratedOnAdd().HasColumnType("integer").HasDefaultValueSql("nextval('\"AnimalsDb\".\"PersonSequence\"')");NpgsqlPropertyBuilderExtensions.UseSequence(b.Property<int>("Id"));b.Property<int>("Age").HasColumnType("integer");b.Property<string>("Name").HasColumnType("text");b.HasKey("Id");b.ToTable("People","AnimalsDb");b.UseTpcMappingStrategy();});modelBuilder.Entity("EFCoreTpcAbstractInclude.Entities.Pet", b =>{b.Property<int>("Id").ValueGeneratedOnAdd().HasColumnType("integer").HasDefaultValueSql("nextval('\"AnimalsDb\".\"PetSequence\"')");NpgsqlPropertyBuilderExtensions.UseSequence(b.Property<int>("Id"));b.Property<int>("Age").HasColumnType("integer");b.Property<string>("Name").HasColumnType("text");b.Property<int>("OwnerId").HasColumnType("integer");b.HasKey("Id");b.HasIndex("OwnerId");b.ToTable((string)null);b.UseTpcMappingStrategy();});modelBuilder.Entity("EFCoreTpcAbstractInclude.Entities.Cat", b =>{b.HasBaseType("EFCoreTpcAbstractInclude.Entities.Pet");b.Property<bool>("LovesSleeping").HasColumnType("boolean");b.ToTable("Cats","AnimalsDb");});modelBuilder.Entity("EFCoreTpcAbstractInclude.Entities.Dog", b =>{b.HasBaseType("EFCoreTpcAbstractInclude.Entities.Pet");b.Property<bool>("LovesChasingSticks").HasColumnType("boolean");b.ToTable("Dogs","AnimalsDb");});modelBuilder.Entity("EFCoreTpcAbstractInclude.Entities.Pet", b =>{b.HasOne("EFCoreTpcAbstractInclude.Entities.Person","Owner").WithMany().HasForeignKey("OwnerId").OnDelete(DeleteBehavior.Cascade).IsRequired();b.Navigation("Owner");});
#pragma warning restore 612,618}}}
I’d expect .Include(p => p.Owner) to work consistently, even if Person has navigation properties to specific Dog/Cat types (the DB looks the same, caused by the exact same migration, so it should be possible to traverse the reference by the Id). Since removing those collections fixes the error, it seems like an EF Core bug (or at least an undocumented limitation) when combining TPC with inheritance-based navigation properties.
I have a TPC hierarchy consisting of an abstract
Pet
entity (withDog
andCat
as subclasses) and aPerson
entity. ThePerson
has navigation collections toDogs
andCats
. With these collection navigation properties present, attempting to queryPets
and.Include(p => p.Owner)
on the abstract type throws:If I remove the collection navigations (
public ICollection<Dog> Dogs { get; set; } = [];
andpublic ICollection<Cat> Cats { get; set; } = [];
) fromPerson
, the query with.Include(p => p.Owner)
succeeds without error.You can see the full example in the following gist, or here: EFCoreTpcAbstractInclude.zip
Here’s the model:
In the attached examples, I have two tests:
IncludeWithCasting
that always passes, andIncludeWithoutCasting
that fails, but in case theDogs
andCats
collection references are removed from thePerson
entities (and theInit
migration is recreated), it passes. The tests, except for the setup, do:With casting, always passes
Without casting, fails and throws an exception if a collection reference exists
The migration is the same in both cases, with and without the
ICollection
references. However, I noticed differences in the generated model snapshots between the two scenarios (with and without the collection navigations onPerson
). The snapshot with the collections places the foreign key in the derivedDog
/Cat
entities, while the version without the collections uses a singleHasOne("EFCoreTpcAbstractInclude.Entities.Person", "Owner")
reference on thePet
entity. This leads me to believe there might be an issue with how EF Core handles TPC, navigation properties, and inheritance in this scenario.Here's the model snapshot with the collection reference:
And here's the model, but without the collection reference:
I’d expect
.Include(p => p.Owner)
to work consistently, even ifPerson
has navigation properties to specificDog
/Cat
types (the DB looks the same, caused by the exact same migration, so it should be possible to traverse the reference by theId
). Since removing those collections fixes the error, it seems like an EF Core bug (or at least an undocumented limitation) when combining TPC with inheritance-based navigation properties.I'm using:
Microsoft.EntityFrameworkCore.Design Version="9.0.0"
Microsoft.EntityFrameworkCore.Relational Version="9.0.0"
Npgsql.EntityFrameworkCore.PostgreSQL Version="9.0.2"
Microsoft.EntityFrameworkCore.Abstractions Version="9.0.0"
net9.0
The text was updated successfully, but these errors were encountered: