Sieve icon indicating copy to clipboard operation
Sieve copied to clipboard

Filtering and sorting on inherited properties cannot be translated to SQL with EF Core

Open SuperGouge opened this issue 5 years ago • 9 comments

Properties declared on a base class cannot be used in filters and sorts when applying them to a query of a derived type. The expressions generated by Sieve are unable to be translated to SQL by EF Core (and probably some other providers using the same LINQ parser). This causes a client evaluation of the query and depending on the configuration and version, this will either cause huge performance issues or simply throw an exception.

Here is an example illustrating exactly what the problem is:

public class Foo
{
    public int Id { get; set; }
    public string Bar { get; set; }
}

public class MyDbContext : DbContext
{
    public DbSet<Foo> Foos { get; set; }
}
public class FooModel
{
    public string Bar { get; set; }
}

public class FooBarModel : FooModel
{
    public string BarFoo { get; set; }
}
public IList<FooBarModel> GetModels(MyDbContext dbContext, ISieveProcessor sieveProcessor, SieveModel sieveModel)
{
    var query = dbContext.Foos
        .Select(f => new FooBarModel
        {
            Bar = f.Bar,
            BarFoo = f.Bar + f.Bar
        });
    query = sieveProcessor.Apply(sieveModel, query, applyPagination: false);
    return query.ToList();
}
var dbContext = GetMyDbContext();
var sieveProcessor = GetSieveProcessor();

var validSieveModel = new SieveModel
{
    Filters = "BarFoo==test"
};
var validResult = GetModels(dbContext, sieveProcessor, validSieveModel); // Will be correctly translated to SQL.

var invalidSieveModel = new SieveModel
{
    Filters = "Bar==test"
};
var invalidResult = GetModels(dbContext, sieveProcessor, invalidSieveModel); // Cannot be translated to SQL. Will generate a Microsoft.EntityFrameworkCore.Query.QueryClientEvaluationWarning or throw an exception depending on version/config.

More information as well as a possible solution can be found here and here. It looks like the expressions manually generated by Sieve would need to be adapted to reference the properties with the right type.

SuperGouge avatar May 23 '19 09:05 SuperGouge

Has this feature been implemented yet, or if not then has it been in any forks of Sieve that you know of?

zackattackz avatar Feb 22 '23 04:02 zackattackz

I'm not too familiar with Sieve exactly, I've just been scouting different filtering libraries to find one that best fits my needs - so forgive my lack of knowledge here.

I'm essentially looking for the ability to have filters for a type hierarchy (which is supported by ef core)

The filters should be able to be applied to check against specific instances of a sub type in the hierarchy, and many could be applied across the different sub types possible.

If this isn't too clear I could try to explain further with examples, thank you for your time.

zackattackz avatar Feb 23 '23 18:02 zackattackz

Thank you for the clarification but I believe maybe I'm not describing my needs the best way. I would like for the Expression Tree to be generated with filters of sub classes in mind. (Using Expression.TypeIs)

I can't explain with words much better, but if you are interested and have some time, please check out this demo project I put together, using my fork of FilterExpressionCreator (a similar library to Sieve)

Specifically, check out this file in particular, which will generate the expression trees that I need. (check out the console output when accessing that page)

Also, here is the issue I opened in FilterExpressionCreator describing this issue and my feature in more depth.

I may just move forward with FilterExpressionCreator, but I'd like to contribute to Sieve if possible, unless this feature already exists.

zackattackz avatar Feb 23 '23 20:02 zackattackz

x => ((x Is Reptile) AndAlso Invoke(x => ((x.ScaleHardness > 10) AndAlso (((x Is Turtle) AndAlso Invoke(x => (x.ShellHardness > 10), Convert(x, Turtle))) OrElse ((x Is Crocodile) AndAlso Invoke(x => (x.BiteForce < 10), Convert(x, Crocodile))))), Convert(x, Reptile)))

Is an example of the expression tree generated in /Animals/8 of that demo project.

See how it will check if x Is SubType before then checking the SubType specific filters (combing them with AndAlso)

zackattackz avatar Feb 23 '23 20:02 zackattackz

Yeah it was more so a request. I don't really need the feature, was just suggesting. Figured someone in the future might need it. If it is, as you say, not necessary: What would you suggest someone who has an existing project with a class hierarchy do if they need this feature?

zackattackz avatar Feb 26 '23 03:02 zackattackz