-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Open
Description
Bug description
This is similar to #32977 but without the collection projection.
I'm including two repros. I first discovered the issue with AutoMapper and then tried to make a repro without it to keep it minimal. However, my repro without AutoMapper has a different expression tree. I believe they likely fail due to the same root cause, but it may be good to verify any fix against both repros. (I could probably try harder to do a repro that produces the same expression tree as AutoMapper, but then you'd start to question why anyone would write a query that way!)
Your code
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
await using var ctx = new BlogContext();
await ctx.Database.EnsureDeletedAsync();
await ctx.Database.EnsureCreatedAsync();
var queryA = ctx.EntityA
.Select(x => new CombinedDto
{
Id = x.Id,
EntityCDto = x.EntityCs
.OrderBy(x => x.Id)
.Select(x => new EntityCDto { Value1 = x.Value1, Value2 = x.Value2 })
.FirstOrDefault()
});
var queryB = ctx.EntityB
.Select(x => new CombinedDto
{
Id = x.Id,
EntityCDto = x.EntityCs
.OrderBy(x => x.Id)
.Select(x => new EntityCDto { Value1 = x.Value1, Value2 = x.Value2 })
.FirstOrDefault()
});
// Query A works on its own
_ = await queryA.ToListAsync();
// Query B works on its own
_ = await queryB.ToListAsync();
// Query A + B fails
var concatQuery = queryA.Concat(queryB);
_ = await concatQuery.ToListAsync();
public class BlogContext : DbContext
{
public DbSet<EntityA> EntityA { get; set; }
public DbSet<EntityB> EntityB { get; set; }
public DbSet<EntityC> EntityC { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=SetFailure;Trusted_Connection=True;Encrypt=False")
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder
.Entity<EntityA>()
.HasMany(x => x.EntityCs);
modelBuilder
.Entity<EntityB>()
.HasMany(x => x.EntityCs);
}
}
public class EntityBase
{
public Guid Id { get; set; }
}
public class EntityA : EntityBase
{
public ICollection<EntityC> EntityCs { get; set; } = [];
}
public class EntityB : EntityBase
{
public ICollection<EntityC> EntityCs { get; set; } = [];
}
public class EntityC : EntityBase
{
public required string Value1 { get; set; }
public required string Value2 { get; set; }
}
public class CombinedDto
{
public Guid Id { get; set; }
public EntityCDto? EntityCDto { get; set; }
}
public class EntityCDto
{
public required string Value1 { get; set; }
public required string Value2 { get; set; }
}
Using AutoMapper 15.0.0
nuget
using AutoMapper;
using AutoMapper.QueryableExtensions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
var mapper = new MapperConfiguration(
a =>
{
a.CreateMap<EntityA, CombinedDto>()
.ForMember(x => x.EntityCDto, o => o.MapFrom(x => x.EntityCs.OrderBy(x => x.Id).FirstOrDefault()));
a.CreateMap<EntityB, CombinedDto>()
.ForMember(x => x.EntityCDto, o => o.MapFrom(x => x.EntityCs.OrderBy(x => x.Id).FirstOrDefault()));
a.CreateMap<EntityC, EntityCDto>();
}, new LoggerFactory())
.CreateMapper();
await using var ctx = new BlogContext();
await ctx.Database.EnsureDeletedAsync();
await ctx.Database.EnsureCreatedAsync();
var queryA = ctx.EntityA.ProjectTo<CombinedDto>(mapper.ConfigurationProvider);
var queryB = ctx.EntityB.ProjectTo<CombinedDto>(mapper.ConfigurationProvider);
// Query A works on its own
_ = await queryA.ToListAsync();
// Query B works on its own
_ = await queryB.ToListAsync();
// Query A + B fails
var concatQuery = queryA.Concat(queryB);
_ = await concatQuery.ToListAsync();
// snip - same context and models as above
Stack traces
System.InvalidOperationException
HResult=0x80131509
Message=Unable to translate set operation after client projection has been applied. Consider moving the set operation before the last 'Select' call.
Source=Microsoft.EntityFrameworkCore.Relational
StackTrace:
at Microsoft.EntityFrameworkCore.Query.SqlExpressions.SelectExpression.ApplySetOperation(SetOperationType setOperationType, SelectExpression select2, Boolean distinct)
at Microsoft.EntityFrameworkCore.Query.SqlExpressions.SelectExpression.ApplyUnion(SelectExpression source2, Boolean distinct)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.TranslateConcat(ShapedQueryExpression source1, ShapedQueryExpression source2)
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.Translate(Expression expression)
at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutorExpression[TResult](Expression query)
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__DisplayClass11_0`1.<ExecuteCore>b__0()
at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteCore[TResult](Expression query, Boolean async, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetAsyncEnumerator(CancellationToken cancellationToken)
at System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable`1.GetAsyncEnumerator() in /_/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConfiguredCancelableAsyncEnumerable.cs:line 44
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.<ToListAsync>d__67`1.MoveNext()
at Program.<<Main>$>d__0.MoveNext() in C:\Users\steven.darby\source\repos\Playground\Playground\Program.cs:line 39
at Program.<<Main>$>d__0.MoveNext() in C:\Users\steven.darby\source\repos\Playground\Playground\Program.cs:line 39
at Program.<Main>(String[] args)
Verbose output
EF Core version
9.0.6
Database provider
SqlServer
Target framework
No response
Operating system
No response
IDE
No response