dotnet / dotnet/efcore

Query throws exception when using TPC and abstract base class has an Owned entity type property.

Open
#29,818 6 comments 1 reaction 0 assignees View on GitHub
area-query customer-reported
Dominant language
C#
Stars
14.8k
Forks
3.4k
PR merge metrics
PR metrics pending

Description

## Details
When using the table-per-concrete-type (TPC) mapping strategy with an abstract base class, where that base class includes a property to an Owned entity type, a query against that hierarchy throws an InvalidOperationException with message "Sequence contains no matching element".

### Provider and version information

EF Core version: 7.0
Database provider: Microsoft.EntityFrameworkCore.Sqlite, Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 7.0
Operating system: Windows 10 22H2, 2 different machines
IDE: Visual Studio 2022 17.4.2, LinqPad 7.5

### Variations tried
- Tested w/ different providers:
- Does not occur with Microsoft.EntityFrameworkCore.InMemory
- Does occur with Sqlite and SqlServer providers
- Entities can be added and saved okay. The issue only occurs on querying.
- The issue occurs when querying any DbSet related to the type hierarchy - the base class or the derived.

### Workarounds Verified
- remove the the `abstract` from the base class
- using the default table mapping strategy
- When mapping the Owned-type property, it works to .Ignore() it in the abstract base class, and map it in each derived concrete class.

### Untested Scenarios
- I didn't test if the problem occurs with a property of a non-Owned entity type.

## Code to reproduce
See attached ZIP for a VS solution/project containing this same code.

[EfCoreIssueRepro.zip](https://github.com/dotnet/efcore/files/10197548/EfCoreIssueRepro.zip)

```C#
#define USE_TPC
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
Console.WriteLine("Started...");

var dbContextOptions = new DbContextOptionsBuilder()
.UseSqlite($"DataSource=file:{Guid.NewGuid()}?Mode=Memory")
.Options;

var dbContext = new AppDbContext(dbContextOptions);
dbContext.Database.EnsureDeleted();
dbContext.Database.EnsureCreated();
dbContext.Book.AddRange(new Book { Title = "Book A" }, new Book { Title = "Book B" }, new Book { Title = "Book C" });
dbContext.SaveChanges();
dbContext.ChangeTracker.Clear();

Book? bookA = null;
bookA = dbContext.Book.Where(b => b.Title.Contains("A")).FirstOrDefault();
Console.WriteLine($"bookA: {bookA?.Title ?? ""}");

var bookAId = bookA?.Id ?? Guid.Empty;
var bookANode = dbContext.Node.FirstOrDefault(n => n.Id == bookAId);
Console.WriteLine($"bookANode: {bookANode?.Id.ToString() ?? ""}");

public abstract class Node
{
public Guid Id { get; set; }
public Annotation? Annotation { get; set; }
}

public class Book : Node { public string Title { get; set; } }
public class Annotation { public string Content { get; set; } }

public class NodeConfiguration : IEntityTypeConfiguration
{
public void Configure(EntityTypeBuilder builder)
{
#if USE_TPC
builder.UseTpcMappingStrategy();
#endif

builder.Property(x => x.Id);
builder.HasKey(x => x.Id);

builder.OwnsOne(x => x.Annotation, ConfigureAnnotation);
builder.Navigation(x => x.Annotation).AutoInclude(true);
}
void ConfigureAnnotation(OwnedNavigationBuilder builder)
{
builder.ToTable(nameof(Annotation));
builder.WithOwner();
builder.Property(x => x.Content).IsRequired();
}
}

public class BookConfigration : IEntityTypeConfiguration
{
public void Configure(EntityTypeBuilder builder)
{
builder.ToTable(nameof(Book));
builder.Property(x => x.Title);
}
}

class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions options) : base(options) { }

public DbSet Node => Set();
public DbSet Book => Set();

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(GetType().Assembly);
}
}
```

## Stack trace

```
Unhandled exception. System.InvalidOperationException: Sequence contains no matching element
at System.Linq.ThrowHelper.ThrowNoMatchException()
at System.Linq.Enumerable.First[TSource](IEnumerable`1 source, Func`2 predicate)
at Microsoft.EntityFrameworkCore.Query.SqlExpressions.SelectExpression.g__GetPropertyExpressions|86_0(ISqlExpressionFactory sqlExpressionFactory, INavigation navigation, SelectExpression selectExpression, ColumnExpression identifyingColumn)
at Microsoft.EntityFrameworkCore.Query.SqlExpressions.SelectExpression.GenerateOwnedReferenceEntityProjectionExpression(EntityProjectionExpression principalEntityProjection, INavigation navigation, ISqlExpressionFactory sqlExpressionFactory)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.SharedTypeEntityExpandingExpressionVisitor.TryExpand(Expression source, MemberIdentity member)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.SharedTypeEntityExpandingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.IncludeExpression.VisitChildren(ExpressionVisitor visitor)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.SharedTypeEntityExpandingExpressionVisitor.VisitExtension(Expression extensionExpression)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.SharedTypeEntityExpandingExpressionVisitor.Expand(SelectExpression selectExpression, Expression lambdaBody)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.ExpandSharedTypeEntities(SelectExpression selectExpression, Expression lambdaBody)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.RemapLambdaBody(ShapedQueryExpression shapedQueryExpression, LambdaExpression lambdaExpression)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.TranslateSelect(ShapedQueryExpression source, LambdaExpression selector)
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass9_0`1.b__0()
at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
at Program.$(String[] args) in D:\Repos\EfCoreIssueRepro\EfCoreIssueRepro\Program.cs:line 18
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.