Skip to content

Commit

Permalink
Query: Convert Set to entity queryables in explicitly compiled query
Browse files Browse the repository at this point in the history
Resolves #19322
  • Loading branch information
smitpatel committed Feb 11, 2020
1 parent 4667074 commit 444c29f
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 16 deletions.
29 changes: 14 additions & 15 deletions src/EFCore/Query/Internal/ParameterExtractingExpressionVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public ParameterExtractingExpressionVisitor(
bool generateContextAccessors)
{
_evaluatableExpressionFindingExpressionVisitor
= new EvaluatableExpressionFindingExpressionVisitor(evaluatableExpressionFilter, model);
= new EvaluatableExpressionFindingExpressionVisitor(evaluatableExpressionFilter, model, parameterize);

_parameterValues = parameterValues;
_logger = logger;
Expand Down Expand Up @@ -467,16 +467,19 @@ private sealed class EvaluatableExpressionFindingExpressionVisitor : ExpressionV
private readonly IEvaluatableExpressionFilter _evaluatableExpressionFilter;
private readonly ISet<ParameterExpression> _allowedParameters = new HashSet<ParameterExpression>();
private readonly IModel _model;
private readonly bool _parameterize;

private bool _evaluatable;
private bool _containsClosure;
private bool _inLambda;
private IDictionary<Expression, bool> _evaluatableExpressions;

public EvaluatableExpressionFindingExpressionVisitor(IEvaluatableExpressionFilter evaluatableExpressionFilter, IModel model)
public EvaluatableExpressionFindingExpressionVisitor(
IEvaluatableExpressionFilter evaluatableExpressionFilter, IModel model, bool parameterize)
{
_evaluatableExpressionFilter = evaluatableExpressionFilter;
_model = model;
_parameterize = parameterize;
}

public IDictionary<Expression, bool> Find(Expression expression)
Expand Down Expand Up @@ -504,7 +507,9 @@ public override Expression Visit(Expression expression)

_evaluatable = IsEvaluatableNodeType(expression)
// Extension point to disable funcletization
&& _evaluatableExpressionFilter.IsEvaluatableExpression(expression, _model);
&& _evaluatableExpressionFilter.IsEvaluatableExpression(expression, _model)
// Don't evaluate QueryableMethods if in compiled query
&& (_parameterize || !IsQueryableMethod(expression));
_containsClosure = false;

base.Visit(expression);
Expand Down Expand Up @@ -643,19 +648,13 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio
}

private static bool IsEvaluatableNodeType(Expression expression)
{
if (expression.NodeType == ExpressionType.Extension)
{
if (!expression.CanReduce)
{
return false;
}
=> expression.NodeType != ExpressionType.Extension
|| expression.CanReduce
&& IsEvaluatableNodeType(expression.ReduceAndCheck());

return IsEvaluatableNodeType(expression.ReduceAndCheck());
}

return true;
}
private static bool IsQueryableMethod(Expression expression)
=> expression is MethodCallExpression methodCallExpression
&& methodCallExpression.Method.DeclaringType == typeof(Queryable);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public virtual void DbSet_query()
public virtual void DbSet_query_first()
{
var query = EF.CompileQuery(
(NorthwindContext context) => context.Customers.OrderBy(c => c.CustomerID).First());
(NorthwindContext context) => context.Set<Customer>().OrderBy(c => c.CustomerID).First());

using (var context = CreateContext())
{
Expand Down Expand Up @@ -540,6 +540,23 @@ public virtual void Compiled_query_when_does_not_end_in_query_operator()
}
}

[ConditionalFact(Skip = "Issue#19209")]
public virtual void Compiled_query_when_using_member_on_context()
{
var query = EF.CompileQuery(
(NorthwindContext context)
=> context.Customers.Where(c => c.CustomerID.StartsWith(context.TenantPrefix)));

using (var context = CreateContext())
{
context.TenantPrefix = "A";
Assert.Equal(6, query(context).Count());

context.TenantPrefix = "B";
Assert.Equal(4, query(context).Count());
}
}

protected NorthwindContext CreateContext() => Fixture.CreateContext();
}
}
23 changes: 23 additions & 0 deletions test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2214,6 +2214,29 @@ public virtual void Query_cache_entries_are_evicted_as_necessary()
}
}

[ConditionalFact]
public virtual void Explicitly_compiled_query_does_not_add_cache_entry()
{
var parameter = Expression.Parameter(typeof(Entity8909));
var predicate = Expression.Lambda<Func<Entity8909, bool>>(
Expression.MakeBinary(ExpressionType.Equal,
Expression.PropertyOrField(parameter, "Id"),
Expression.Constant(1)),
parameter);
var query = EF.CompileQuery((MyContext8909 context) => context.Set<Entity8909>().SingleOrDefault(predicate));
using (CreateDatabase8909())
{
using var context = new MyContext8909(_options);
context.Cache.Compact(1);
Assert.Equal(0, context.Cache.Count);

query(context);

// 1 entry for RelationalCommandCache
Assert.Equal(1, context.Cache.Count);
}
}

private SqlServerTestStore CreateDatabase8909()
{
return CreateTestStore(
Expand Down

0 comments on commit 444c29f

Please sign in to comment.