-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
OrderBy with contains and mapping fails duplicate Query source - expression association #12175
Comments
@bmarder Any reason you are still using preview2? Can you update to the 2.1 RTM version and report back if you are still seeing the issue? |
@ajcvickers Well the version suggestion certainly explains why I was having trouble replicating in a new app. That bug has definitely been fixed in 2.1. Sorry about that. I can now replicate the 2nd bug, @ajcvickers updated the repo without automapper using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Console;
namespace EFCoreBugs
{
class Program
{
static async Task Main(string[] args)
{
using (var db = new TestDb())
{
await db.Database.EnsureDeletedAsync();
await db.Database.EnsureCreatedAsync();
var blog = new Blog();
var post = new Post();
blog.Posts.Add(post);
db.Blogs.Add(blog);
await db.SaveChangesAsync();
}
using (var db = new TestDb())
{
// throws only if list is empty
var ids = new List<Guid>();
await db.Blogs
.OrderByDescending(x => ids.Contains(x.Id))
.Select(blog => new BlogDto { Posts = blog.Posts.Select(_ => new PostDto()).ToList() })
.ToListAsync();
}
Console.ReadLine();
}
}
public class TestDb : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var log = new LoggerFactory(new[] { new ConsoleLoggerProvider((_, __) => true, true) });
optionsBuilder.UseLoggerFactory(log);
optionsBuilder.UseSqlServer("Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=master;Database=bugtest;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False");
}
}
public class Blog
{
public Guid Id { get; set; }
[InverseProperty(nameof(Post.Blog))]
public virtual List<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public Guid Id { get; set; }
public Guid BlogId { get; set; }
[ForeignKey(nameof(BlogId))]
public virtual Blog Blog { get; private set; }
}
public class BlogDto
{
public IEnumerable<PostDto> Posts { get; set; }
}
public class PostDto { }
}
|
Notes for triage:
var query = db.Blogs
.OrderByDescending(x => ids.Contains(x.Id))
.Select(blog => new BlogDto { Posts = blog.Posts.Select(p => new PostDto() { Title = p.Title }).ToList() });
|
@maumar Can you investigate if this is a regression? |
@ajcvickers this is not a regression, the exception was different in 2.0.2 but the scenario was failing nonetheless. |
simpler query: var ids = new List<Guid>();
var query = db.Blogs
.OrderByDescending(x => ids.Contains(x.Id))
.Select(b => b.Posts.Select(p => p.Id).ToList()); Correlated collection optimization is applied to this query, producing the following QM: from Blog x in DbSet<Blog>
order by
(from Guid <generated>_1 in __ids_0
select [<generated>_1]).Contains([x].Id) desc, (Nullable<Guid>)EF.Property(?[x]?, "Id") asc
select new BlogDto{ Posts = List<PostDto> queryContext.QueryBuffer.CorrelateSubquery(
correlatedCollectionId: 0,
navigation: Blog.Posts,
resultCollectionFactory: (INavigation n) => new List<PostDto>(),
outerKey: new MaterializedAnonymousObject(new object[]{ (object)EF.Property(?[x]?, "Id") }),
tracking: False,
correlatedCollectionFactory: () =>
from Post x.Posts in DbSet<Post>
join MaterializedAnonymousObject _x in
from Blog x in DbSet<Blog>
select new MaterializedAnonymousObject(new object[]
{
(object)
(from Guid <generated>_1 in __ids_0
select [<generated>_1]).Contains([x].Id),
(object)(Nullable<Guid>)EF.Property(?[x]?, "Id")
})
on Property([x.Posts], "BlogId") equals (Nullable<Guid>)object [_x].GetValue(1)
order by object [_x].GetValue(0) desc, object [_x].GetValue(1) asc
select new Tuple<PostDto, MaterializedAnonymousObject, MaterializedAnonymousObject>(
PostDto,
new MaterializedAnonymousObject(new object[]{ (object)EF.Property(?[x.Posts]?, "BlogId") }),
new MaterializedAnonymousObject(new object[]{ object [_x].GetValue(1) })
),
correlationPredicate: (MaterializedAnonymousObject o | MaterializedAnonymousObject i) => object i.GetValue(0) == default(object) || object o.GetValue(0) == default(object) ? False : (Guid)object o.GetValue(0) == (Guid)object i.GetValue(0)) } and 2 queries: SELECT [x].[Id]
FROM [Blogs] AS [x]
ORDER BY (SELECT 1) DESC, [x].[Id] SELECT [t].[c], [t].[Id], [x.Posts].[BlogId]
FROM [Posts] AS [x.Posts]
INNER JOIN (
SELECT 0 AS [c], [x0].[Id]
FROM [Blogs] AS [x0]
) AS [t] ON [x.Posts].[BlogId] = [t].[Id]
ORDER BY [t].[c] DESC, [t].[Id] Problem here is that in the second query, If normal constant bool is projected in a subquery we have safeguards in |
…ry source - expression association Problem was that for queries that project constant bool which gets produced as a result of optimization (e.g. Contains on empty collection) we would not add explicit cast to bool, which resulted in value coming back as int. Fix is to apply optimizations before we perform the check that determines whether the explicit cast is needed - this way the value is already collapsed into constant at this point and explicit cast is correctly applied.
…ry source - expression association Problem was that for queries that project constant bool which gets produced as a result of optimization (e.g. Contains on empty collection) we would not add explicit cast to bool, which resulted in value coming back as int. Fix is to apply optimizations before we perform the check that determines whether the explicit cast is needed - this way the value is already collapsed into constant at this point and explicit cast is correctly applied.
…ry source - expression association Problem was that for queries that project constant bool which gets produced as a result of optimization (e.g. Contains on empty collection) we would not add explicit cast to bool, which resulted in value coming back as int. Fix is to apply optimizations before we perform the check that determines whether the explicit cast is needed - this way the value is already collapsed into constant at this point and explicit cast is correctly applied.
…ry source - expression association Problem was that for queries that project constant bool which gets produced as a result of optimization (e.g. Contains on empty collection) we would not add explicit cast to bool, which resulted in value coming back as int. Fix is to apply optimizations before we perform the check that determines whether the explicit cast is needed - this way the value is already collapsed into constant at this point and explicit cast is correctly applied.
fixed in e18824e |
…ry source - expression association Problem was that for queries that project constant bool which gets produced as a result of optimization (e.g. Contains on empty collection) we would not add explicit cast to bool, which resulted in value coming back as int. Fix is to apply optimizations before we perform the check that determines whether the explicit cast is needed - this way the value is already collapsed into constant at this point and explicit cast is correctly applied.
…ry source - expression association Problem was that for queries that project constant bool which gets produced as a result of optimization (e.g. Contains on empty collection) we would not add explicit cast to bool, which resulted in value coming back as int. Fix is to apply optimizations before we perform the check that determines whether the explicit cast is needed - this way the value is already collapsed into constant at this point and explicit cast is correctly applied.
…ry source - expression association Problem was that for queries that project constant bool which gets produced as a result of optimization (e.g. Contains on empty collection) we would not add explicit cast to bool, which resulted in value coming back as int. Fix is to apply optimizations before we perform the check that determines whether the explicit cast is needed - this way the value is already collapsed into constant at this point and explicit cast is correctly applied.
I had all of the above happen while trying to replicate a different error below. If somehow this works (with the mappings added), two SQL queries are generated which throws a completely different error. Also note, the following error only occurs if the
ids
list is empty.Further technical details
EF Core version: 2.1.0-preview2-final
Database Provider: Microsoft.EntityFrameworkCore.SqlServer
Operating system:
IDE: (e.g. Visual Studio 2017 15.4)
The text was updated successfully, but these errors were encountered: