-
Notifications
You must be signed in to change notification settings - Fork 180
Closed
Labels
bugSomething isn't workingSomething isn't working
Description
Assemblies affected
- ASP.NET Core OData 8.x
- ASP.NET Core OData 9.x
Describe the bug
The translation of the isof method adds an unnecessary conditional expression.
This additional expression can have important side effects depending on the underlying IQueryableProvider.
I identified this bug by seeing very inefficient and abnormally complex queries using MongoDb. I suppose other providers optimize the case and get rid of this issue (I hope).
Reproduce steps
- Considering the data model below
- Have OData controller with:
[EnableQuery] public IQueryable<Zoo> Get() { return zooCollection.AsQueryable(); }
- Odata filter:
Animals/any(a:isof(a,'MyCompany.Cat')) - Expression parsed: z => z.Animals.Any(a => a is Cat ? true : false)
- Generated mongo query:
{ "$expr" : { "$anyElementTrue" : { "$map" : { "input" : "$Animals", "as" : "se", "in" : { "$cond" : { "if" : { "$eq" : ["$$se._t", "Cat"] }, "then" : true, "else" : false } } } } } }
Note: The real problem is that query cannot use database indexes - Expected mongo query:
{ "Animals" : { "$elemMatch" : { "_t" : "Cat" } } }
Data Model
namespace MyCompany;
public sealed record Zoo
{
public ObjectId Id { get; set; }
public ICollection<Animal> Animals { get; set; } = [];
}
public abstract class Animal
{
public required string Name { get; set; }
}
public sealed class Cat : Animal
{
public required bool HasMustaches { get; set; }
}
public sealed class Dog : Animal
{
public required string Color { get; set; }
}Additional context
I have a workaround that simplify the expression after all using a ExpressionVisitor similarly as done in MongoDB.AspNetCore.OData/MongoEnableQueryAttribute.cs
internal sealed class SimplifyExpressionVisitor : ExpressionVisitor
{
protected override Expression VisitConditional(ConditionalExpression node)
{
if (node.Type == typeof(bool) && node.IfTrue.NodeType == ExpressionType.Constant && node.IfFalse.NodeType == ExpressionType.Constant)
{
bool ifTrueValue = Convert.ToBoolean(((ConstantExpression)node.IfTrue).Value);
bool ifFalseValue = Convert.ToBoolean(((ConstantExpression)node.IfFalse).Value);
return (ifTrueValue, ifFalseValue) switch
{
(true, true) => node.IfTrue,
(false, false) => node.IfTrue,
(true, false) => Visit(node.Test),
(false, true) => Expression.Not(Visit(node.Test))
};
}
return base.VisitConditional(node);
}
}Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't working