Skip to content
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

Improve expression debugging #31082

Merged
merged 1 commit into from
Jun 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/EFCore.Relational/Query/JsonQueryExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,10 @@ public virtual JsonQueryExpression MakeNullable()
/// <inheritdoc />
public virtual void Print(ExpressionPrinter expressionPrinter)
{
expressionPrinter.Append("JsonQueryExpression(");
expressionPrinter.Visit(JsonColumn);
expressionPrinter.Append($""", "{string.Join(".", Path.Select(e => e.ToString()))}")""");
expressionPrinter
.Append(" Q-> ")
.Append(string.Join(".", Path.Select(e => e.ToString())));
}

/// <inheritdoc />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions;
/// not used in application code.
/// </para>
/// </summary>
[DebuggerDisplay("{DebuggerDisplay(),nq}")]
[DebuggerDisplay("{TableAlias}.{Name}")]
public abstract class ColumnExpression : SqlExpression
{
/// <summary>
Expand Down Expand Up @@ -64,7 +64,4 @@ protected override void Print(ExpressionPrinter expressionPrinter)
expressionPrinter.Append(TableAlias).Append(".");
expressionPrinter.Append(Name);
}

private string DebuggerDisplay()
=> $"{TableAlias}.{Name}";
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,10 @@ public virtual JsonScalarExpression Update(SqlExpression json)
/// <inheritdoc />
protected override void Print(ExpressionPrinter expressionPrinter)
{
expressionPrinter.Append("JsonScalarExpression(column: ");
expressionPrinter.Visit(Json);
expressionPrinter.Append($""", "{string.Join(".", Path.Select(e => e.ToString()))}")""");
expressionPrinter
.Append(" -> ")
.Append(string.Join(".", Path.Select(e => e.ToString())));
}

/// <inheritdoc />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions;
/// not used in application code.
/// </para>
/// </summary>
[DebuggerDisplay("{Microsoft.EntityFrameworkCore.Query.ExpressionPrinter.Print(this), nq}")]
public class OrderingExpression : Expression, IPrintableExpression
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions;
/// application or provider, then please file an issue at
/// <see href="https://github.com/dotnet/efcore">github.com/dotnet/efcore</see>.
/// </remarks>
[DebuggerDisplay("{Microsoft.EntityFrameworkCore.Query.ExpressionPrinter.Print(this), nq}")]
public sealed class ProjectionExpression : Expression, IPrintableExpression
{
internal ProjectionExpression(SqlExpression expression, string alias)
Expand Down Expand Up @@ -57,6 +58,7 @@ public ProjectionExpression Update(SqlExpression expression)
void IPrintableExpression.Print(ExpressionPrinter expressionPrinter)
{
expressionPrinter.Visit(Expression);

if (Alias != string.Empty
&& !(Expression is ColumnExpression column
&& column.Name == Alias))
Expand Down
40 changes: 36 additions & 4 deletions src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions;
/// an issue at <see href="https://github.com/dotnet/efcore">github.com/dotnet/efcore</see>.
/// </remarks>
// Class is sealed because there are no public/protected constructors. Can be unsealed if this is changed.
[DebuggerDisplay("{PrintShortSql(), nq}")]
public sealed partial class SelectExpression : TableExpressionBase
{
private const string DiscriminatorColumnAlias = "Discriminator";
Expand Down Expand Up @@ -4561,6 +4562,13 @@ public override IEnumerable<IAnnotation> GetAnnotations()

/// <inheritdoc />
protected override void Print(ExpressionPrinter expressionPrinter)
{
PrintProjections(expressionPrinter);
expressionPrinter.AppendLine();
PrintSql(expressionPrinter);
}

private void PrintProjections(ExpressionPrinter expressionPrinter)
{
if (_clientProjections.Count > 0)
{
Expand Down Expand Up @@ -4588,12 +4596,16 @@ protected override void Print(ExpressionPrinter expressionPrinter)
}
}
}
}

expressionPrinter.AppendLine();

foreach (var tag in Tags)
private void PrintSql(ExpressionPrinter expressionPrinter, bool withTags = true)
{
if (withTags)
{
expressionPrinter.Append($"-- {tag}");
foreach (var tag in Tags)
{
expressionPrinter.Append($"-- {tag}");
}
}

IDisposable? indent = null;
Expand Down Expand Up @@ -4682,6 +4694,26 @@ protected override void Print(ExpressionPrinter expressionPrinter)
}
}

private string PrintShortSql()
{
var expressionPrinter = new ExpressionPrinter();
PrintSql(expressionPrinter, withTags: false);
return expressionPrinter.ToString();
}

/// <summary>
/// <para>
/// Expand this property in the debugger for a human-readable representation of this <see cref="SelectExpression" />.
/// </para>
/// <para>
/// Warning: Do not rely on the format of the debug strings.
/// They are designed for debugging only and may change arbitrarily between releases.
/// </para>
/// </summary>
[EntityFrameworkInternal]
public string DebugView
=> this.Print();

/// <inheritdoc />
public override bool Equals(object? obj)
=> obj != null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions;
/// not used in application code.
/// </para>
/// </summary>
#if DEBUG
[DebuggerDisplay("{new Microsoft.EntityFrameworkCore.Query.ExpressionPrinter().PrintExpression(this), nq}")]
#endif
[DebuggerDisplay("{Microsoft.EntityFrameworkCore.Query.ExpressionPrinter.Print(this), nq}")]
public abstract class SqlExpression : Expression, IPrintableExpression
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions;
/// not used in application code.
/// </para>
/// </summary>
[DebuggerDisplay("{Microsoft.EntityFrameworkCore.Query.ExpressionPrinter.Print(this), nq}")]
public abstract class TableExpressionBase : Expression, IPrintableExpression
{
private readonly IReadOnlyDictionary<string, IAnnotation>? _annotations;
Expand Down
18 changes: 12 additions & 6 deletions src/EFCore/Query/ExpressionPrinter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -166,16 +166,22 @@ private string PrintCore(Expression expression, int? characterLimit = null, bool

Visit(expression);

var queryPlan = PostProcess(_stringBuilder.ToString());
return ToString();
}

/// <inheritdoc />
public override string ToString()
{
var printed = PostProcess(_stringBuilder.ToString());

if (characterLimit is > 0)
if (CharacterLimit is > 0)
{
queryPlan = queryPlan.Length > characterLimit
? queryPlan[..characterLimit.Value] + "..."
: queryPlan;
printed = printed.Length > CharacterLimit
? printed[..CharacterLimit.Value] + "..."
: printed;
}

return queryPlan;
return printed;
}

/// <summary>
Expand Down
1 change: 0 additions & 1 deletion src/EFCore/Query/ProjectionMember.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ namespace Microsoft.EntityFrameworkCore.Query;
/// See <see href="https://aka.ms/efcore-docs-providers">Implementation of database providers and extensions</see>
/// and <see href="https://aka.ms/efcore-docs-how-query-works">How EF Core queries work</see> for more information and examples.
/// </remarks>
[DebuggerDisplay("{ToString(), nq}")]
public sealed class ProjectionMember
{
private readonly IList<MemberInfo> _memberChain;
Expand Down
1 change: 1 addition & 0 deletions src/EFCore/Query/ShapedQueryExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace Microsoft.EntityFrameworkCore.Query;
/// See <see href="https://aka.ms/efcore-docs-providers">Implementation of database providers and extensions</see>
/// and <see href="https://aka.ms/efcore-docs-how-query-works">How EF Core queries work</see> for more information and examples.
/// </remarks>
[DebuggerDisplay("{Microsoft.EntityFrameworkCore.Query.ExpressionPrinter.Print(QueryExpression), nq}")]
public class ShapedQueryExpression : Expression, IPrintableExpression
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -761,7 +761,7 @@ public virtual async Task Json_collection_element_access_in_projection_using_unt
ss => ss.Set<JsonEntityBasic>().Select(x => x.OwnedCollectionRoot[MyMethod(x.Id)]).AsNoTracking()))).Message;

Assert.Equal(
CoreStrings.TranslationFailed("""JsonQueryExpression(j.OwnedCollectionRoot, "")"""),
CoreStrings.TranslationFailed("j.OwnedCollectionRoot Q-> "),
message);
}

Expand All @@ -775,7 +775,7 @@ public virtual async Task Json_collection_element_access_in_projection_using_unt
ss => ss.Set<JsonEntityBasic>().Select(x => x.OwnedCollectionRoot[0].OwnedReferenceBranch.OwnedCollectionLeaf[MyMethod(x.Id)]).AsNoTracking()))).Message;

Assert.Equal(
CoreStrings.TranslationFailed("""JsonQueryExpression(j.OwnedCollectionRoot, "[0].OwnedReferenceBranch.OwnedCollectionLeaf")"""),
CoreStrings.TranslationFailed("j.OwnedCollectionRoot Q-> [0].OwnedReferenceBranch.OwnedCollectionLeaf"),
message);
}

Expand Down Expand Up @@ -967,7 +967,7 @@ public virtual async Task Json_collection_element_access_manual_Element_at_and_p
CollectionElement = x.OwnedCollectionRoot[prm].OwnedCollectionBranch.Select(xx => "Foo").ElementAt(0)
})))).Message;

Assert.Equal(CoreStrings.TranslationFailed("""JsonQueryExpression(j.OwnedCollectionRoot, "[__prm_0].OwnedCollectionBranch")"""), message);
Assert.Equal(CoreStrings.TranslationFailed("j.OwnedCollectionRoot Q-> [__prm_0].OwnedCollectionBranch"), message);
}

[ConditionalTheory]
Expand All @@ -984,7 +984,7 @@ public virtual async Task Json_collection_element_access_manual_Element_at_and_p
CollectionElement = x.OwnedCollectionRoot[prm + x.Id].OwnedCollectionBranch.Select(xx => x.Id).ElementAt(0)
})))).Message;

Assert.Equal(CoreStrings.TranslationFailed("""JsonQueryExpression(j.OwnedCollectionRoot, "[(...)].OwnedCollectionBranch")"""), message);
Assert.Equal(CoreStrings.TranslationFailed("j.OwnedCollectionRoot Q-> [(...)].OwnedCollectionBranch"), message);
}

[ConditionalTheory]
Expand All @@ -1000,7 +1000,7 @@ public virtual async Task Json_collection_element_access_manual_Element_at_and_p
CollectionElement = x.OwnedCollectionRoot.Select(xx => x.OwnedReferenceRoot).ElementAt(0)
})))).Message;

Assert.Equal(CoreStrings.TranslationFailed("""JsonQueryExpression(j.OwnedCollectionRoot, "")"""), message);
Assert.Equal(CoreStrings.TranslationFailed("j.OwnedCollectionRoot Q-> "), message);
}

[ConditionalTheory]
Expand All @@ -1016,7 +1016,7 @@ public virtual async Task Json_collection_element_access_manual_Element_at_and_p
CollectionElement = x.OwnedCollectionRoot.Select(xx => x.OwnedCollectionRoot).ElementAt(0)
})))).Message;

Assert.Equal(CoreStrings.TranslationFailed("""JsonQueryExpression(j.OwnedCollectionRoot, "")"""), message);
Assert.Equal(CoreStrings.TranslationFailed("j.OwnedCollectionRoot Q-> "), message);
}

[ConditionalTheory]
Expand All @@ -1032,7 +1032,7 @@ public virtual async Task Json_collection_element_access_manual_Element_at_and_p
CollectionElement = x.OwnedCollectionRoot.Select(xx => new { xx.OwnedReferenceBranch }).ElementAt(0)
})))).Message;

Assert.Equal(CoreStrings.TranslationFailed("""JsonQueryExpression(j.OwnedCollectionRoot, "")"""), message);
Assert.Equal(CoreStrings.TranslationFailed("j.OwnedCollectionRoot Q-> "), message);
}

[ConditionalTheory]
Expand All @@ -1048,7 +1048,7 @@ public virtual async Task Json_collection_element_access_manual_Element_at_and_p
CollectionElement = x.OwnedCollectionRoot.Select(xx => new JsonEntityBasic { Id = x.Id }).ElementAt(0)
})))).Message;

Assert.Equal(CoreStrings.TranslationFailed("""JsonQueryExpression(j.OwnedCollectionRoot, "")"""), message);
Assert.Equal(CoreStrings.TranslationFailed("j.OwnedCollectionRoot Q-> "), message);
}

[ConditionalTheory]
Expand Down