Skip to content

Commit 8bf8a4c

Browse files
committed
Fix to #15264 - QueryRewrite: incorporate query filters into nav rewrite
When nav rewrite constructs new EntityQueryable it now looks into EntityType for any query filter annotations and applies them on top. Those predicates are parameterized and the parameters created are injected into the context as part of query execution expression. Generated predicate expression is re-visited by nav expansion to recursively handle filters (expand potential navigation expansions that were introduced in them). This adds some complexity to the way navigations are expanded - before the newly constructed join always was guaranteed to be an entity. Now it can be a complex structure with multiple navigations already expanded. Also small cleanup on ExpressionPrinter - adding IPrintable to some of the custom nodes created by nav expansion and removing rendering of connecting lines when printing the expression.
1 parent 841963d commit 8bf8a4c

28 files changed

+565
-442
lines changed

src/EFCore/Internal/EntityFinder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ private IQueryable<object[]> GetDatabaseValuesQuery(InternalEntityEntry entry)
222222
keyValues[i] = keyValue;
223223
}
224224

225-
return _queryRoot.AsNoTracking()//.IgnoreQueryFilters()
225+
return _queryRoot.AsNoTracking().IgnoreQueryFilters()
226226
.Where(BuildObjectLambda(properties, new ValueBuffer(keyValues)))
227227
.Select(BuildProjection(entityType));
228228
}

src/EFCore/Internal/IndentedStringBuilder.cs

Lines changed: 1 addition & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5-
using System.Collections.Generic;
65
using System.IO;
76
using System.Text;
87
using JetBrains.Annotations;
@@ -18,11 +17,6 @@ namespace Microsoft.EntityFrameworkCore.Internal
1817
public class IndentedStringBuilder
1918
{
2019
private const byte IndentSize = 4;
21-
22-
private readonly string _disconnectedIndent = new string(' ', IndentSize);
23-
private readonly string _suspendedIndent = "|" + new string(' ', IndentSize - 1);
24-
private readonly string _connectedIndent = "|" + new string('_', IndentSize - 2) + " ";
25-
2620
private byte _indent;
2721
private bool _indentPending = true;
2822

@@ -158,94 +152,19 @@ public virtual IndentedStringBuilder Clear()
158152
return this;
159153
}
160154

161-
private enum NodeConnectionState
162-
{
163-
Disconnected = 0,
164-
Connected = 1,
165-
Suspended = 2
166-
}
167-
168-
private readonly List<NodeConnectionState> _nodeConnectionStates = new List<NodeConnectionState>();
169-
170155
/// <summary>
171156
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
172157
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
173158
/// any release. You should only use it directly in your code with extreme caution and knowing that
174159
/// doing so can result in application failures when updating to a new Entity Framework Core release.
175160
/// </summary>
176161
public virtual IndentedStringBuilder IncrementIndent()
177-
=> IncrementIndent(false);
178-
179-
/// <summary>
180-
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
181-
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
182-
/// any release. You should only use it directly in your code with extreme caution and knowing that
183-
/// doing so can result in application failures when updating to a new Entity Framework Core release.
184-
/// </summary>
185-
public virtual IndentedStringBuilder IncrementIndent(bool connectNode)
186162
{
187-
var state = connectNode ? NodeConnectionState.Connected : NodeConnectionState.Disconnected;
188-
if (_indent == _nodeConnectionStates.Count)
189-
{
190-
_nodeConnectionStates.Add(state);
191-
}
192-
else
193-
{
194-
_nodeConnectionStates[_indent] = state;
195-
}
196-
197163
_indent++;
198164

199165
return this;
200166
}
201167

202-
/// <summary>
203-
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
204-
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
205-
/// any release. You should only use it directly in your code with extreme caution and knowing that
206-
/// doing so can result in application failures when updating to a new Entity Framework Core release.
207-
/// </summary>
208-
public virtual void DisconnectCurrentNode()
209-
{
210-
if (_indent > 0
211-
&& _nodeConnectionStates.Count >= _indent)
212-
{
213-
_nodeConnectionStates[_indent - 1] = NodeConnectionState.Disconnected;
214-
}
215-
}
216-
217-
/// <summary>
218-
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
219-
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
220-
/// any release. You should only use it directly in your code with extreme caution and knowing that
221-
/// doing so can result in application failures when updating to a new Entity Framework Core release.
222-
/// </summary>
223-
public virtual void SuspendCurrentNode()
224-
{
225-
if (_indent > 0
226-
&& _nodeConnectionStates.Count >= _indent
227-
&& _nodeConnectionStates[_indent - 1] == NodeConnectionState.Connected)
228-
{
229-
_nodeConnectionStates[_indent - 1] = NodeConnectionState.Suspended;
230-
}
231-
}
232-
233-
/// <summary>
234-
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
235-
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
236-
/// any release. You should only use it directly in your code with extreme caution and knowing that
237-
/// doing so can result in application failures when updating to a new Entity Framework Core release.
238-
/// </summary>
239-
public virtual void ReconnectCurrentNode()
240-
{
241-
if (_indent > 0
242-
&& _nodeConnectionStates.Count >= _indent
243-
&& _nodeConnectionStates[_indent - 1] == NodeConnectionState.Suspended)
244-
{
245-
_nodeConnectionStates[_indent - 1] = NodeConnectionState.Connected;
246-
}
247-
}
248-
249168
/// <summary>
250169
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
251170
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -257,8 +176,6 @@ public virtual IndentedStringBuilder DecrementIndent()
257176
if (_indent > 0)
258177
{
259178
_indent--;
260-
261-
_nodeConnectionStates.RemoveAt(_indent);
262179
}
263180

264181
return this;
@@ -284,25 +201,7 @@ private void DoIndent()
284201
{
285202
if (_indentPending && (_indent > 0))
286203
{
287-
var indentString = string.Empty;
288-
for (var i = 0; i < _nodeConnectionStates.Count; i++)
289-
{
290-
if (_nodeConnectionStates[i] == NodeConnectionState.Disconnected)
291-
{
292-
indentString += _disconnectedIndent;
293-
}
294-
else if (i == _nodeConnectionStates.Count - 1
295-
&& _nodeConnectionStates[i] == NodeConnectionState.Connected)
296-
{
297-
indentString += _connectedIndent;
298-
}
299-
else
300-
{
301-
indentString += _suspendedIndent;
302-
}
303-
}
304-
305-
_stringBuilder.Append(indentString);
204+
_stringBuilder.Append(new string(' ', _indent * IndentSize));
306205
}
307206

308207
_indentPending = false;

src/EFCore/Query/ExpressionPrinter.cs

Lines changed: 36 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public class ExpressionPrinter : ExpressionVisitor
1919
private readonly IndentedStringBuilder _stringBuilder;
2020
private readonly Dictionary<ParameterExpression, string> _parametersInScope;
2121
private readonly List<ParameterExpression> _namelessParameters;
22+
private readonly List<ParameterExpression> _encounteredParameters;
2223

2324
private readonly Dictionary<ExpressionType, string> _binaryOperandMap = new Dictionary<ExpressionType, string>
2425
{
@@ -47,6 +48,7 @@ public ExpressionPrinter()
4748
_stringBuilder = new IndentedStringBuilder();
4849
_parametersInScope = new Dictionary<ParameterExpression, string>();
4950
_namelessParameters = new List<ParameterExpression>();
51+
_encounteredParameters = new List<ParameterExpression>();
5052
}
5153

5254
public virtual IndentedStringBuilder StringBuilder => _stringBuilder;
@@ -55,7 +57,7 @@ public ExpressionPrinter()
5557

5658
private int? CharacterLimit { get; set; }
5759

58-
private bool PrintConnections { get; set; }
60+
private bool GenerateUniqueParameterIds { get; set; }
5961

6062
public virtual void VisitList<T>(
6163
IReadOnlyList<T> items,
@@ -105,12 +107,6 @@ public virtual ExpressionPrinter IncrementIndent()
105107
return this;
106108
}
107109

108-
public virtual ExpressionPrinter IncrementIndent(bool connectNode)
109-
{
110-
_stringBuilder.IncrementIndent(connectNode);
111-
return this;
112-
}
113-
114110
public virtual ExpressionPrinter DecrementIndent()
115111
{
116112
_stringBuilder.DecrementIndent();
@@ -132,18 +128,32 @@ private void AppendLine([NotNull] string message)
132128
}
133129

134130
public virtual string Print(
131+
Expression expression,
132+
bool removeFormatting = false,
133+
int? characterLimit = null)
134+
=> PrintCore(expression, removeFormatting, characterLimit, generateUniqueParameterIds: false);
135+
136+
public virtual string PrintDebug(
135137
Expression expression,
136138
bool removeFormatting = false,
137139
int? characterLimit = null,
138-
bool printConnections = true)
140+
bool generateUniqueParameterIds = true)
141+
=> PrintCore(expression, removeFormatting, characterLimit, generateUniqueParameterIds);
142+
143+
protected virtual string PrintCore(
144+
Expression expression,
145+
bool removeFormatting,
146+
int? characterLimit,
147+
bool generateUniqueParameterIds)
139148
{
140149
_stringBuilder.Clear();
141150
_parametersInScope.Clear();
142151
_namelessParameters.Clear();
152+
_encounteredParameters.Clear();
143153

144154
RemoveFormatting = removeFormatting;
145155
CharacterLimit = characterLimit;
146-
PrintConnections = printConnections;
156+
GenerateUniqueParameterIds = generateUniqueParameterIds;
147157

148158
Visit(expression);
149159

@@ -318,12 +328,6 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
318328
protected override Expression VisitBlock(BlockExpression blockExpression)
319329
{
320330
AppendLine();
321-
322-
if (PrintConnections)
323-
{
324-
_stringBuilder.SuspendCurrentNode();
325-
}
326-
327331
AppendLine("{");
328332
_stringBuilder.IncrementIndent();
329333

@@ -359,11 +363,6 @@ protected override Expression VisitBlock(BlockExpression blockExpression)
359363
_stringBuilder.DecrementIndent();
360364
Append("}");
361365

362-
if (PrintConnections)
363-
{
364-
_stringBuilder.ReconnectCurrentNode();
365-
}
366-
367366
return blockExpression;
368367
}
369368

@@ -384,11 +383,6 @@ protected override Expression VisitConditional(ConditionalExpression conditional
384383

385384
protected override Expression VisitConstant(ConstantExpression constantExpression)
386385
{
387-
if (PrintConnections)
388-
{
389-
_stringBuilder.SuspendCurrentNode();
390-
}
391-
392386
if (constantExpression.Value is IPrintable printable)
393387
{
394388
printable.Print(this);
@@ -402,11 +396,6 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio
402396
Print(constantExpression.Value);
403397
}
404398

405-
if (PrintConnections)
406-
{
407-
_stringBuilder.ReconnectCurrentNode();
408-
}
409-
410399
return constantExpression;
411400
}
412401

@@ -474,7 +463,7 @@ protected override Expression VisitLambda<T>(Expression<T> lambdaExpression)
474463
_parametersInScope.Add(parameter, parameterName);
475464
}
476465

477-
_stringBuilder.Append(parameter.Type.ShortDisplayName() + " " + parameterName);
466+
Visit(parameter);
478467

479468
if (parameter != lambdaExpression.Parameters.Last())
480469
{
@@ -620,8 +609,7 @@ var argumentNames
620609

621610
if (!isSimpleMethodOrProperty)
622611
{
623-
var shouldPrintConnections = PrintConnections && !_nonConnectableMethods.Contains(methodCallExpression.Method.Name);
624-
_stringBuilder.IncrementIndent(shouldPrintConnections);
612+
_stringBuilder.IncrementIndent();
625613
}
626614

627615
for (var i = 0; i < methodCallExpression.Arguments.Count; i++)
@@ -633,12 +621,6 @@ var argumentNames
633621
_stringBuilder.Append(argumentNames[i] + ": ");
634622
}
635623

636-
if (i == methodCallExpression.Arguments.Count - 1
637-
&& !isSimpleMethodOrProperty)
638-
{
639-
_stringBuilder.DisconnectCurrentNode();
640-
}
641-
642624
Visit(argument);
643625

644626
if (i < methodCallExpression.Arguments.Count - 1)
@@ -715,12 +697,6 @@ protected override Expression VisitNewArray(NewArrayExpression newArrayExpressio
715697
var appendAction = isComplex ? (Action<string>)AppendLine : Append;
716698

717699
appendAction("new " + newArrayExpression.Type.GetElementType().ShortDisplayName() + "[]");
718-
719-
if (PrintConnections)
720-
{
721-
_stringBuilder.SuspendCurrentNode();
722-
}
723-
724700
appendAction("{ ");
725701

726702
if (isComplex)
@@ -737,11 +713,6 @@ protected override Expression VisitNewArray(NewArrayExpression newArrayExpressio
737713

738714
Append("}");
739715

740-
if (PrintConnections)
741-
{
742-
_stringBuilder.ReconnectCurrentNode();
743-
}
744-
745716
return newArrayExpression;
746717
}
747718

@@ -779,6 +750,21 @@ protected override Expression VisitParameter(ParameterExpression parameterExpres
779750
Append(")");
780751
}
781752

753+
if (GenerateUniqueParameterIds)
754+
{
755+
var parameterIndex = _encounteredParameters.Count;
756+
if (_encounteredParameters.Contains(parameterExpression))
757+
{
758+
parameterIndex = _encounteredParameters.IndexOf(parameterExpression);
759+
}
760+
else
761+
{
762+
_encounteredParameters.Add(parameterExpression);
763+
}
764+
765+
_stringBuilder.Append("{" + parameterIndex + "}");
766+
}
767+
782768
return parameterExpression;
783769
}
784770

@@ -893,7 +879,6 @@ private void VisitArguments(IReadOnlyList<Expression> arguments, Action<string>
893879
if (areConnected && i == arguments.Count - 1)
894880
{
895881
Append("");
896-
_stringBuilder.DisconnectCurrentNode();
897882
}
898883

899884
Visit(arguments[i]);

0 commit comments

Comments
 (0)