-
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
Query: Include doesn't work with SelectMany #6451
Comments
@Bartmax could you also provide listing for DbContext (especially the OnModelCreating) and IFileData? |
I have no code OnModelCreating code regarding any included entities. Here's
|
Verified this is a bug. Simplified repro: class Program
{
static void Main(string[] args)
{
using (var ctx = new MyContext())
{
ctx.Database.EnsureDeleted();
ctx.Database.EnsureCreated();
var bf11 = new SiteBannerFile();
var bf12 = new SiteBannerFile();
var l11 = new SiteBannerLayer { SiteBannerFile = bf11 };
var l12 = new SiteBannerLayer { SiteBannerFile = bf12 };
var b1 = new SiteBanner { Layers = new List<SiteBannerLayer> { l11, l12, } };
ctx.SiteBanners.AddRange(b1);
ctx.SiteBannerLayers.AddRange(l11, l12);
ctx.SiteBannerFiles.AddRange(bf11, bf12);
ctx.SaveChanges();
}
using (var ctx = new MyContext())
{
var query = ctx.SiteBanners.Include(sb => sb.Layers).ThenInclude(l => l.SiteBannerFile)
.SelectMany(b => b.Layers).ToList();
}
}
}
public class MyContext : DbContext
{
public DbSet<SiteBanner> SiteBanners { get; set; }
public DbSet<SiteBannerLayer> SiteBannerLayers { get; set; }
public DbSet<SiteBannerFile> SiteBannerFiles { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=.;Database=Repro6451;Trusted_Connection=True;MultipleActiveResultSets=true");
}
}
public class SiteBanner
{
public int SiteBannerId { get; set; }
public ICollection<SiteBannerLayer> Layers { get; set; }
}
public class SiteBannerLayer
{
public int SiteBannerLayerId { get; set; }
[Required]
public SiteBannerFile SiteBannerFile { get; set; }
}
public class SiteBannerFile
{
public int SiteBannerFileId { get; set; }
} |
@Bartmax as a workaround I would suggest running two queries, one retrieves the entities you are interested in, and the second one does the proper include: var query1 = context.SiteBanners.SelectMany(b => b.Layers).Where(l => l.FieldId == id).Select(l => l.SiteBannerLayerId).FirstOrDefault();
var query2 = context.SiteBannerLayers.Include(l=>l.SiteBannerFile).Where(l => l.SiteBannerLayerId == query1); |
Problem was that we were not able to match query source we were trying to include, if it came from the SelectMany since it has different structure than just regular filter/projection etc. Fix is to compensate for the SelectMany case when constructing IncludeSpecification by using the correct query source.
Clearing the milestone because at this point the fix does not seem to fit in 1.1.0-preview1. We can discuss the appropriate milestone in triage. |
Problem was that in several places we transformed query model but we were not updating the (include) annotations accordingly. This resulted in annotations going out-of-sync with the actual query model which then resulted in us not being able to find the right query source to run include on. Fix is to update query annotations where necessary (nav rewrite, select many rewrite, join elimination). There was additional problem with string based include - we were always assuming that the include operator was called directly on a DbSet, so string based include didn't work for cases like: customers.SelectMany(c => c.Orders).Include("..."); orders.Select(o => o.Customer).Include("..."); Fix is to add additional metadata to the IncludeResultOperator that describes the path from the initial query source (i.e. DbSet) to the actual include method. Also refactored expression based include in similar fashion - split old NavigationPropertyPath into two expressions: PathFromQuerySource (the same as with string based include) and lambda describing properties that are being included.
Problem was that in several places we transformed query model but we were not updating the (include) annotations accordingly. This resulted in annotations going out-of-sync with the actual query model which then resulted in us not being able to find the right query source to run include on. Fix is to update query annotations where necessary (nav rewrite, select many rewrite, join elimination). There was additional problem with string based include - we were always assuming that the include operator was called directly on a DbSet, so string based include didn't work for cases like: customers.SelectMany(c => c.Orders).Include("..."); orders.Select(o => o.Customer).Include("..."); Fix is to add additional metadata to the IncludeResultOperator that describes the path from the initial query source (i.e. DbSet) to the actual include method. Also refactored expression based include - split old NavigationPropertyPath into two: - expression from going from query source to the start of include chain - list of navigation paths that are to be included. Effectively both lambda and string based include use the same logic now. Also fixed a minor bug where string based include was not working on navigations declared on derived types.
Problem was that in several places we transformed query model but we were not updating the (include) annotations accordingly. This resulted in annotations going out-of-sync with the actual query model which then resulted in us not being able to find the right query source to run include on. Fix is to update query annotations where necessary (nav rewrite, select many rewrite, join elimination). There was additional problem with string based include - we were always assuming that the include operator was called directly on a DbSet, so string based include didn't work for cases like: customers.SelectMany(c => c.Orders).Include("..."); orders.Select(o => o.Customer).Include("..."); Fix is to add additional metadata to the IncludeResultOperator that describes the path from the initial query source (i.e. DbSet) to the actual include method. Also refactored expression based include - split old NavigationPropertyPath into two: - expression from going from query source to the start of include chain - list of navigation paths that are to be included. Effectively both lambda and string based include use the same logic now. Also fixed a minor bug where string based include was not working on navigations declared on derived types.
Problem was that in several places we transformed query model but we were not updating the (include) annotations accordingly. This resulted in annotations going out-of-sync with the actual query model which then resulted in us not being able to find the right query source to run include on. Fix is to update query annotations where necessary (nav rewrite, select many rewrite, join elimination). There was additional problem with string based include - we were always assuming that the include operator was called directly on a DbSet, so string based include didn't work for cases like: customers.SelectMany(c => c.Orders).Include("..."); orders.Select(o => o.Customer).Include("..."); Fix is to add additional metadata to the IncludeResultOperator that describes the path from the initial query source (i.e. DbSet) to the actual include method. Also refactored expression based include - split old NavigationPropertyPath into two: - expression from going from query source to the start of include chain - list of navigation paths that are to be included. Effectively both lambda and string based include use the same logic now. Also fixed a minor bug where string based include was not working on navigations declared on derived types.
Fixed in 93ecfec |
Problem was that in several places we transformed query model but we were not updating the (include) annotations accordingly. This resulted in annotations going out-of-sync with the actual query model which then resulted in us not being able to find the right query source to run include on. Fix is to update query annotations where necessary (nav rewrite, select many rewrite, join elimination). There was additional problem with string based include - we were always assuming that the include operator was called directly on a DbSet, so string based include didn't work for cases like: customers.SelectMany(c => c.Orders).Include("..."); orders.Select(o => o.Customer).Include("..."); Fix is to add additional metadata to the IncludeResultOperator that describes the path from the initial query source (i.e. DbSet) to the actual include method. Also refactored expression based include - split old NavigationPropertyPath into two: - expression from going from query source to the start of include chain - list of navigation paths that are to be included. Effectively both lambda and string based include use the same logic now. Also fixed a minor bug where string based include was not working on navigations declared on derived types.
Any chance this fix will get ported back to EF 6? |
@michaelaird EF Core is an entirely different codebase than EF6, so any change to fix a similar issue in EF6 would be a different change, not just a port of this fix. It's very unlikely that this will change in EF6. |
I figured as much. We’re waiting on lifecycle hooks then we’ll be able to
migrate.
…On Fri, Dec 15, 2017 at 4:08 PM Arthur Vickers ***@***.***> wrote:
@michaelaird <https://github.com/michaelaird> EF Core is an entirely
different codebase than EF6, so any change to fix a similar issue in EF6
would be a different change, not just a port of this fix. It's very
unlikely that this will change in EF6.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#6451 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AANX6XmKJQj-6MAYR9CBPmgnsztUfZ_Hks5tAt_AgaJpZM4JzCKD>
.
|
Hi, does this bug fixed, could you give me the version of ef core where this bug was fixed. |
@hieplam In the labels there is a "closed fixed" tag. This means that it has been fixed. The milestone indicates which release contains the fix--in this case 2.0.0-preview1. |
Can this issue be reopened for version 2.2.4 |
@Tbd19 - It's entirely different bug. Please file a new issue with repro code and version of EF core you are using. |
Steps to reproduce
The issue
this does populate the
SiteBannerLayer
entity but not theSiteBannerFile
.SiteBannerFile
is null.In the example above I used Include everywhere just in case.
Of course this works:
but that's an inefficient query
Further technical details
EF Core version: 1.0
Operating system: Windows 10
Visual Studio version: 2015
Other details about my project setup:
Here are the entities involved.
The text was updated successfully, but these errors were encountered: