-
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: Allow use of AsQueryable method #6132
Comments
I could not reproduce this. Below is the code i used. Can you make modifications in it according to what you are trying to do? public class Program
{
public static void Main(string[] args)
{
using (var ctx = new MyContext())
{
ctx.Database.EnsureDeleted();
ctx.Database.EnsureCreated();
var query1 =
ctx.Categories.Where(c => c.ParentId == Guid.Parse("3CA9FA61-EB62-4480-B476-867F78A9ADB3")).ToList();
var query2 = ctx.ProductCategories.Select(pc => pc.Category).Where(Category.IsGenre).ToList();
}
}
}
public class MyContext : DbContext
{
public DbSet<ProductCategory> ProductCategories { get; set; }
public DbSet<Category> Categories { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=_ModelApp;Trusted_Connection=True;");
}
}
public class ProductCategory
{
public Guid Id { get; set; }
public Category Category { get; set; }
}
public class Category
{
private static Guid Genre = Guid.Parse("3CA9FA61-EB62-4480-B476-867F78A9ADB3");
public static Expression<Func<Category, bool>> IsGenre = c => c.ParentId == Genre;
public Guid Id { get; set; }
public Guid ParentId { get; set; }
public ProductCategory Parent { get; set; }
} |
This is actually a many-to-many relationship. Please try the code below (and let me know if you need seed data): using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
namespace EFIssue
{
public class Program
{
public static void Main(string[] args)
{
using (var ctx = new MyContext())
{
var query1 =
ctx.Categories.Where(c => c.ParentId == Guid.Parse("3CA9FA61-EB62-4480-B476-867F78A9ADB3")).ToList();
var query2 = ctx.ProductCategories.Select(pc => pc.Category).Where(Category.IsGenre).ToList();
var query3 = ctx.Products.Select(p =>
p.ProductCategories.Select(pc => pc.Category)
.Where(Category.IsGenre))
.ToList();
}
}
}
public class MyContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<ProductCategory> ProductCategories { get; set; }
public DbSet<Category> Categories { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=_ModelApp;Trusted_Connection=True;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Category>(entity =>
{
entity.HasKey(e => e.CategoryId)
.HasName("PK__Category__19093A0BEC90FC43");
entity.ToTable("Category", "Commerce");
entity.Property(e => e.CategoryId).ValueGeneratedNever();
entity.Property(e => e.Name).HasMaxLength(255);
});
modelBuilder.Entity<Product>(entity =>
{
entity.ToTable("Product", "Commerce");
entity.Property(e => e.ProductId).ValueGeneratedNever();
entity.Property(e => e.Name).HasMaxLength(255);
});
modelBuilder.Entity<ProductCategory>(entity =>
{
entity.ToTable("ProductCategory", "Commerce");
entity.HasIndex(e => new { e.ProductId, e.CategoryId })
.HasName("nci_wi_ProductCategory_E47C12BC-D5AC-4997-BCF5-AD7A7AF8751B");
entity.HasIndex(e => new { e.CategoryId, e.Id, e.ProductId })
.HasName("nci_wi_ProductCategory_C39551F634191AFB3482");
entity.Property(e => e.Id).HasColumnName("id");
entity.HasOne(e => e.Category)
.WithMany(c => c.ProductCategories)
.HasForeignKey(e => e.CategoryId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("FK__ProductCa__Categ__0D44F85C");
entity.HasOne(e => e.Product)
.WithMany(p => p.ProductCategories)
.HasForeignKey(e => e.ProductId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("FK__ProductCa__Produ__0C50D423");
});
}
}
public class Product
{
public Product()
{
ProductCategories = new HashSet<ProductCategory>();
}
public Guid ProductId { get; set; }
public string Name { get; set; }
public virtual ICollection<ProductCategory> ProductCategories { get; set; }
}
public class ProductCategory
{
public Guid CategoryId { get; set; }
public Guid ProductId { get; set; }
public int Id { get; set; }
public virtual Category Category { get; set; }
public virtual Product Product { get; set; }
}
public class Category
{
public Guid CategoryId { get; set; }
public Guid? ParentId { get; set; }
public string Name { get; set; }
public virtual ICollection<ProductCategory> ProductCategories { get; set; }
public static Expression<Func<Category, bool>> IsGenre = c => c.ParentId == Genre;
// public virtual bool IsGenre => ParentId == Genre;
private static Guid Genre = Guid.Parse("3CA9FA61-EB62-4480-B476-867F78A9ADB3");
}
} |
@tinchou - Thanks for working repro code. Can you tell in words what you are trying to query so I can translate it to linq. |
In my real app I would like to:
|
I tried running different queries. The queries in repro code are based criteria on category (rather than product as you mentioned in previous post) but since relationship is many to many, it can be easily written from other way depending on what you want to query.
As you can see you can still get products for categories even with starting from categories db set. |
@smitpatel anything to do on the EF side here? |
From discussions so far, the way user wants to write query is not possible due to limited from re-linq. And there is alternate way to write the query which gives same results. |
I was curious about this one because it ringed a bell so I took a look: In the past I found it very useful to create repositories that take predicates of the form It was very handy that I agree with @smitpatel that in some cases it should be possible to change your code to get the same data loaded from the other side, but I don't think that it is true in all cases, e.g. it won't help if the reverse nav. prop. isn't present. It might also be harder to figure out what query to write. If this is a fundamental issue in re-linq with |
@smitpatel could you pass off a request to re-linq to look at removing this limitation, then you can close this as external. |
Fine with me. Please post here if you open an issue on re-linq so I can keep an eye on it! |
BTW re-linq issue tracker is at https://www.re-motion.org/jira/browse/RMLNQ |
Found the existing bug in re-linq |
cc @MichaelKetting as heads up that we would appreciate if https://www.re-motion.org/jira/projects/RMLNQ/issues/RMLNQ-28 got looked at to unblock us from supporting |
@divega I've taken a quick look at the solution proposed in our Jira ticket and if the custom AsQueryableNode really is all that's needed, you don't need to wait for re-linq to implement this. In EF's QueryCompiler.CreateNodeTypeProvider you already use the extension point for custom node types. Mind you, I haven't gotten to actually trying the sample from our ticket, but if this works, this would certainly give you the most effective way forward. BTW, thanks for pointing out that this isn't implemented yet. I've prioritized it for the next release but if the quick fix works, I'd prefer to treat this as nice-to-have. Edit: Come to think about it, 'most effective' might not be correct since EF just did a 1.0 release and if I fix this in re-linq, tincho could just upgrade re-linq and needn't wait for the next EF release. Interesting thing, this semantic versioning in combination with NuGet... |
Is there any update? Or maybe a workaround (that doesn't involve building own EF)? |
Reopening the issue and removing from milestone for triage. We need to decide if we want to do something to translate P.S. - if we decide to add support with custom extension, we should file new issue tracking it. |
@MichaelKetting - Any estimate when fix for https://www.re-motion.org/jira/projects/RMLNQ/issues/RMLNQ-28?filter=allopenissues will be available or should we go ahead and write code on our end as suggested? |
I've got some vacation days coming up the last week of October so that should work out nicely for me having some time on my hands :) |
@techniq yeah, I just managed to push the AsQueryable feature in 2.2.0-alpha.5 :) Edit: fixed version number. |
I just got the email on the merge. Thanks much. I'll give it a shot with
binding redirects (hopefully) in the next day or so. I really appreciate
you taking care of this.
…On Sun, Jan 21, 2018, 4:58 PM Michael Ketting ***@***.***> wrote:
@techniq <https://github.com/techniq> yeah, I just managed to push the
AsQueryable feature in 2.0.0-alpha.5 :)
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#6132 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAK1RF8kWkRAUy6nwKfjbuqipLD2RCufks5tM7L_gaJpZM4JSO36>
.
|
@MichaelKetting Just a heads up, I added the I'm not sure if the problem lies within EFCore, OData / ASP.NET Core, or Remotion.Linq. Any thoughts? |
@techniq Hmm...I've usually seen this with StackOverflowExceptions. You could try to install Remotion.Linq v2.2.0-alpha.4 to check if the problem is related to the AsQueryable-feature. That was the only change in alpha.5, so can do an A-B test very easily. Then there's the option of attaching a debugger and debugging the exceptions. A StackOverflow excpetion should show up in the debugger. Erm...I just noticed, I may have written "v2.0.0-alpha.5" instead of "v2.2.0-alpha.5" in my previous reply. Could you check that one first? Edit: we never had a v2.0.0-alpha.5, so at least you couldn't have downloaded the wrong version. Maybe the wrong binding redirect, though. |
@MichaelKetting As you determined, I definitely have the I was using
With that, it appears to be a problem on the ASP.NET Core OData side and I'll log an issue over there (although I'm not for certain if it's not the result of the interplay between EFCore, OData, and Remotion.Linq). |
Thanks for the update. The AccessViolationException sounds definitely strange. My best guesses would either be an undetected API change in the alpha.5 (which would really surprise me if I missed that) or some issue when working with unmanged code during the query execution. But I'll adopt a wait-and-see approch for now :) |
@MichaelKetting - Thanks for the fix. We are now able to get parsed query model. Can you also take a look at #9311. We are at present in preview2 and would soon need RTM version of re-linq to use in our RTM version. (else we would have to revert back to 2.1.1) Can you give an estimate when can RTM version be available? Removing milestone of this issue to track work needed to get translation to server. |
@smitpatel I can do an RTM build as needed, the question is, should I try to take a shot at the two issues in https://www.re-motion.org/jira/issues/?filter=11440 over the weekend and drop a beta version for you to try first and then an RTM build sometime next week? Or just go with what's already in the bag? |
@MichaelKetting - Sorry for late response. Sure go ahead with more bug fixes if you want. One week wait is no issue for us. Let me know if you drop another beta version, I can certainly help validate it against EF tests. |
Fix it if it's easy in 2.1, else punt. |
Confirmed fixed after we take the new version of relinq. |
Okay, didn't manage the OrderBy with Custom Comparer and the QueryModel Bug for v2.2. Sorry about that, but on the plus side the v2.2 rtm build's just gotten sent off to the build server. |
Thanks @MichaelKetting |
Added test in e2a2b9b |
Update
Modifying the description to track support for
AsQueryable
in linq queries.AsQueryable
method is required when user wants to pass Expression to linq method which is hanging of an navigation property since navigation is of type IEnumerable. With the relinq fix now we can get parsed query with AsQueryable but we are client eval-ing afterwards. This issue is to track work needed on EF Core side to translate query to server.Query
var query3 = db.Products.Select(p => p.ProductCategories.AsQueryable().Select(pc => pc.Category).Where(Category.IsGenre)).ToList();
QueryExecution:
Original Issue
Hi!
I'm trying to map an existing database to EF Core. Our project has a terrible model where we have to compare the
ParentId
to a specificGuid
to find out the type of a row. E.g. we have the tableCategories
and each Guid identifiesGenre
,Mood
, etc. (we have a music app).So I'm trying to write this property in the Category class, but if I do, I'm unable to use
Include
because it can't be translated:If I instead do
ctx.Categories.Where(c => c.ParentId == Guid.Parse("3CA9FA61-EB62-4480-B476-867F78A9ADB3")
it works perfectly.I'm wondering if there's any way to move this to the Category class so I can avoid copy and pasting for each query I need to write.
I tried to manually create the
Expression
:but I get the following error on
p.ProductCategories.Select(pc => pc.Category).Where(Category.IsGenre)
:With
.AsQueryable()
I'm able to compile the program but I getThis overload of the method 'System.Linq.Queryable.AsQueryable' is currently not supported
.Thanks!
The text was updated successfully, but these errors were encountered: