diff --git a/src/EFCore/Diagnostics/CoreLoggerExtensions.cs b/src/EFCore/Diagnostics/CoreLoggerExtensions.cs index 1ba85664bf1..e41f0fb569c 100644 --- a/src/EFCore/Diagnostics/CoreLoggerExtensions.cs +++ b/src/EFCore/Diagnostics/CoreLoggerExtensions.cs @@ -462,7 +462,7 @@ public static (Expression Query, QueryExpressionEventData? EventData) QueryCompi if (diagnostics.ShouldLog(definition)) { - definition.Log(diagnostics, Environment.NewLine, expressionPrinter.Print(queryExpression)); + definition.Log(diagnostics, Environment.NewLine, expressionPrinter.PrintExpression(queryExpression)); } if (diagnostics.NeedsEventData( @@ -490,7 +490,7 @@ private static string QueryCompilationStarting(EventDefinitionBase definition, E { var d = (EventDefinition)definition; var p = (QueryExpressionEventData)payload; - return d.GenerateMessage(Environment.NewLine, p.ExpressionPrinter.Print(p.Expression)); + return d.GenerateMessage(Environment.NewLine, p.ExpressionPrinter.PrintExpression(p.Expression)); } /// @@ -668,7 +668,7 @@ public static void QueryExecutionPlanned( if (diagnostics.ShouldLog(definition)) { - definition.Log(diagnostics, Environment.NewLine, expressionPrinter.Print(queryExecutorExpression)); + definition.Log(diagnostics, Environment.NewLine, expressionPrinter.PrintExpression(queryExecutorExpression)); } if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled)) @@ -688,7 +688,7 @@ private static string QueryExecutionPlanned(EventDefinitionBase definition, Even { var d = (EventDefinition)definition; var p = (QueryExpressionEventData)payload; - return d.GenerateMessage(Environment.NewLine, p.ExpressionPrinter.Print(p.Expression)); + return d.GenerateMessage(Environment.NewLine, p.ExpressionPrinter.PrintExpression(p.Expression)); } /// diff --git a/src/EFCore/Infrastructure/ExpressionExtensions.cs b/src/EFCore/Infrastructure/ExpressionExtensions.cs index 5b7cf16c4c5..fb4266ef148 100644 --- a/src/EFCore/Infrastructure/ExpressionExtensions.cs +++ b/src/EFCore/Infrastructure/ExpressionExtensions.cs @@ -29,7 +29,7 @@ public static class ExpressionExtensions /// An optional limit to the number of characters included. Additional output will be truncated. /// The printable representation. public static string Print(this Expression expression, int? characterLimit = null) - => new ExpressionPrinter().Print(expression, characterLimit); + => new ExpressionPrinter().PrintExpression(expression, characterLimit); /// /// Creates a that represents accessing either a field or a property. diff --git a/src/EFCore/Query/ExpressionPrinter.cs b/src/EFCore/Query/ExpressionPrinter.cs index ae786634157..514c158bcb8 100644 --- a/src/EFCore/Query/ExpressionPrinter.cs +++ b/src/EFCore/Query/ExpressionPrinter.cs @@ -70,34 +70,6 @@ public ExpressionPrinter() private int? CharacterLimit { get; set; } private bool Verbose { get; set; } - /// - /// Visit given readonly collection of expression for printing. - /// - /// A collection of items to print. - /// A join action to use when joining printout of individual item in the collection. - public virtual void VisitCollection( - IReadOnlyCollection items, - Action? joinAction = null) - where T : Expression - { - joinAction ??= (p => p.Append(", ")); - - var first = true; - foreach (var item in items) - { - if (!first) - { - joinAction(this); - } - else - { - first = false; - } - - Visit(item); - } - } - /// /// Appends a new line to current output being built. /// @@ -149,30 +121,40 @@ public virtual ExpressionPrinter Append(string value) return this; } + /// + /// Creates a printable string representation of the given expression. + /// + /// The expression to print. + /// The printable representation. + public static string Print(Expression expression) + => new ExpressionPrinter().PrintCore(expression); + + /// + /// Creates a printable verbose string representation of the given expression. + /// + /// The expression to print. + /// The printable representation. + public static string PrintDebug(Expression expression) + => new ExpressionPrinter().PrintCore(expression, verbose: true); + /// /// Creates a printable string representation of the given expression. /// /// The expression to print. /// An optional limit to the number of characters included. Additional output will be truncated. /// The printable representation. - public virtual string Print( - Expression expression, - int? characterLimit = null) - => PrintCore(expression, characterLimit, verbose: false); + public virtual string PrintExpression(Expression expression, int? characterLimit = null) + => PrintCore(expression, characterLimit); /// /// Creates a printable verbose string representation of the given expression. /// /// The expression to print. /// The printable representation. - public virtual string PrintDebug( - Expression expression) - => PrintCore(expression, characterLimit: null, verbose: true); - - private string PrintCore( - Expression expression, - int? characterLimit, - bool verbose) + public virtual string PrintExpressionDebug(Expression expression) + => PrintCore(expression, verbose: true); + + private string PrintCore(Expression expression, int? characterLimit = null, bool verbose = false) { _stringBuilder.Clear(); _parametersInScope.Clear(); @@ -186,8 +168,7 @@ private string PrintCore( var queryPlan = PostProcess(_stringBuilder.ToString()); - if (characterLimit != null - && characterLimit.Value > 0) + if (characterLimit is > 0) { queryPlan = queryPlan.Length > characterLimit ? queryPlan[..characterLimit.Value] + "..." @@ -205,6 +186,32 @@ private string PrintCore( public virtual string GenerateBinaryOperator(ExpressionType expressionType) => _binaryOperandMap[expressionType]; + /// + /// Visit given readonly collection of expression for printing. + /// + /// A collection of items to print. + /// A join action to use when joining printout of individual item in the collection. + public virtual void VisitCollection(IReadOnlyCollection items, Action? joinAction = null) + where T : Expression + { + joinAction ??= (p => p.Append(", ")); + + var first = true; + foreach (var item in items) + { + if (!first) + { + joinAction(this); + } + else + { + first = false; + } + + Visit(item); + } + } + /// [return: NotNullIfNotNull("expression")] public override Expression? Visit(Expression? expression) @@ -386,7 +393,7 @@ protected override Expression VisitBlock(BlockExpression blockExpression) } } - var expressions = blockExpression.Result != null + var expressions = blockExpression.Expressions.Count > 0 ? blockExpression.Expressions.Except(new[] { blockExpression.Result }) : blockExpression.Expressions; @@ -396,7 +403,7 @@ protected override Expression VisitBlock(BlockExpression blockExpression) AppendLine(";"); } - if (blockExpression.Result != null) + if (blockExpression.Expressions.Count > 0) { if (blockExpression.Result.Type != typeof(void)) { @@ -438,50 +445,50 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio } else { - Print(constantExpression.Value); + PrintValue(constantExpression.Value); } return constantExpression; - } - private void Print(object? value) - { - if (value is IEnumerable enumerable - && !(value is string)) + void PrintValue(object? value) { - _stringBuilder.Append(value.GetType().ShortDisplayName() + " { "); - - var first = true; - foreach (var item in enumerable) + if (value is IEnumerable enumerable + && !(value is string)) { - if (first) - { - first = false; - } - else + _stringBuilder.Append(value.GetType().ShortDisplayName() + " { "); + + var first = true; + foreach (var item in enumerable) { - _stringBuilder.Append(", "); + if (first) + { + first = false; + } + else + { + _stringBuilder.Append(", "); + } + + PrintValue(item); } - Print(item); + _stringBuilder.Append(" }"); + return; } - _stringBuilder.Append(" }"); - return; - } + var stringValue = value == null + ? "null" + : value.ToString() != value.GetType().ToString() + ? value.ToString() + : value.GetType().ShortDisplayName(); - var stringValue = value == null - ? "null" - : value.ToString() != value.GetType().ToString() - ? value.ToString() - : value.GetType().ShortDisplayName(); + if (value is string) + { + stringValue = $@"""{stringValue}"""; + } - if (value is string) - { - stringValue = $@"""{stringValue}"""; + _stringBuilder.Append(stringValue ?? "Unknown"); } - - _stringBuilder.Append(stringValue ?? "Unknown"); } /// @@ -642,8 +649,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp _stringBuilder.AppendLine(); _stringBuilder.Append($".{method.Name}"); methodArguments = methodArguments.Skip(1).ToList(); - if (method.Name == nameof(Enumerable.Cast) - || method.Name == nameof(Enumerable.OfType)) + if (method.Name is nameof(Enumerable.Cast) or nameof(Enumerable.OfType)) { PrintGenericArguments(method, _stringBuilder); } diff --git a/src/EFCore/Query/Internal/QueryableMethodNormalizingExpressionVisitor.cs b/src/EFCore/Query/Internal/QueryableMethodNormalizingExpressionVisitor.cs index 659fd7cdb2d..b8ae88c3639 100644 --- a/src/EFCore/Query/Internal/QueryableMethodNormalizingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/QueryableMethodNormalizingExpressionVisitor.cs @@ -165,7 +165,7 @@ private static void VerifyReturnType(Expression expression, ParameterExpression { throw new InvalidOperationException( CoreStrings.QueryInvalidMaterializationType( - new ExpressionPrinter().Print(Expression.Lambda(expression, lambdaParameter)), + new ExpressionPrinter().PrintExpression(Expression.Lambda(expression, lambdaParameter)), expression.Type.ShortDisplayName())); } diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs index f2b4d242396..d98df4b96e4 100644 --- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs @@ -769,7 +769,7 @@ public virtual Task Update_Where_multiple_set(bool async) [MemberData(nameof(IsAsyncData))] public virtual Task Update_with_invalid_lambda_in_set_property_throws(bool async) => AssertTranslationFailed( - RelationalStrings.InvalidPropertyInSetProperty(new ExpressionPrinter().Print((OrderDetail e) => e.MaybeScalar(e => e.OrderID))), + RelationalStrings.InvalidPropertyInSetProperty(new ExpressionPrinter().PrintExpression((OrderDetail e) => e.MaybeScalar(e => e.OrderID))), () => AssertUpdate( async, ss => ss.Set().Where(od => od.OrderID < 10250), diff --git a/test/EFCore.Specification.Tests/QueryExpressionInterceptionTestBase.cs b/test/EFCore.Specification.Tests/QueryExpressionInterceptionTestBase.cs index 23400d5b962..b4d0de24434 100644 --- a/test/EFCore.Specification.Tests/QueryExpressionInterceptionTestBase.cs +++ b/test/EFCore.Specification.Tests/QueryExpressionInterceptionTestBase.cs @@ -125,7 +125,7 @@ public virtual Expression QueryCompilationStarting( { QueryCompilationStartingCalled = true; Context = eventData.Context; - QueryExpression = eventData.ExpressionPrinter.Print(queryExpression); + QueryExpression = eventData.ExpressionPrinter.PrintExpression(queryExpression); return queryExpression; } diff --git a/test/EFCore.Tests/Query/ExpressionPrinterTest.cs b/test/EFCore.Tests/Query/ExpressionPrinterTest.cs index 7fe79a12de2..693b7964e76 100644 --- a/test/EFCore.Tests/Query/ExpressionPrinterTest.cs +++ b/test/EFCore.Tests/Query/ExpressionPrinterTest.cs @@ -10,12 +10,12 @@ public class ExpressionPrinterTest [ConditionalFact] public void UnaryExpression_printed_correctly() { - Assert.Equal("(decimal)42", _expressionPrinter.Print(Expression.Convert(Expression.Constant(42), typeof(decimal)))); - Assert.Equal("throw \"Some exception\"", _expressionPrinter.Print(Expression.Throw(Expression.Constant("Some exception")))); - Assert.Equal("!(True)", _expressionPrinter.Print(Expression.Not(Expression.Constant(true)))); + Assert.Equal("(decimal)42", _expressionPrinter.PrintExpression(Expression.Convert(Expression.Constant(42), typeof(decimal)))); + Assert.Equal("throw \"Some exception\"", _expressionPrinter.PrintExpression(Expression.Throw(Expression.Constant("Some exception")))); + Assert.Equal("!(True)", _expressionPrinter.PrintExpression(Expression.Not(Expression.Constant(true)))); Assert.Equal( "(BaseClass as DerivedClass)", - _expressionPrinter.Print(Expression.TypeAs(Expression.Constant(new BaseClass()), typeof(DerivedClass)))); + _expressionPrinter.PrintExpression(Expression.TypeAs(Expression.Constant(new BaseClass()), typeof(DerivedClass)))); } private class BaseClass @@ -31,65 +31,65 @@ public void BinaryExpression_printed_correctly() { Assert.Equal( "7 == 42", - _expressionPrinter.Print(Expression.MakeBinary(ExpressionType.Equal, Expression.Constant(7), Expression.Constant(42)))); + _expressionPrinter.PrintExpression(Expression.MakeBinary(ExpressionType.Equal, Expression.Constant(7), Expression.Constant(42)))); Assert.Equal( "7 != 42", - _expressionPrinter.Print(Expression.MakeBinary(ExpressionType.NotEqual, Expression.Constant(7), Expression.Constant(42)))); + _expressionPrinter.PrintExpression(Expression.MakeBinary(ExpressionType.NotEqual, Expression.Constant(7), Expression.Constant(42)))); Assert.Equal( "7 > 42", - _expressionPrinter.Print( + _expressionPrinter.PrintExpression( Expression.MakeBinary(ExpressionType.GreaterThan, Expression.Constant(7), Expression.Constant(42)))); Assert.Equal( "7 >= 42", - _expressionPrinter.Print( + _expressionPrinter.PrintExpression( Expression.MakeBinary(ExpressionType.GreaterThanOrEqual, Expression.Constant(7), Expression.Constant(42)))); Assert.Equal( "7 < 42", - _expressionPrinter.Print(Expression.MakeBinary(ExpressionType.LessThan, Expression.Constant(7), Expression.Constant(42)))); + _expressionPrinter.PrintExpression(Expression.MakeBinary(ExpressionType.LessThan, Expression.Constant(7), Expression.Constant(42)))); Assert.Equal( "7 <= 42", - _expressionPrinter.Print( + _expressionPrinter.PrintExpression( Expression.MakeBinary(ExpressionType.LessThanOrEqual, Expression.Constant(7), Expression.Constant(42)))); Assert.Equal( "True && True", - _expressionPrinter.Print( + _expressionPrinter.PrintExpression( Expression.MakeBinary(ExpressionType.AndAlso, Expression.Constant(true), Expression.Constant(true)))); Assert.Equal( "True || True", - _expressionPrinter.Print( + _expressionPrinter.PrintExpression( Expression.MakeBinary(ExpressionType.OrElse, Expression.Constant(true), Expression.Constant(true)))); Assert.Equal( "7 & 42", - _expressionPrinter.Print(Expression.MakeBinary(ExpressionType.And, Expression.Constant(7), Expression.Constant(42)))); + _expressionPrinter.PrintExpression(Expression.MakeBinary(ExpressionType.And, Expression.Constant(7), Expression.Constant(42)))); Assert.Equal( "7 | 42", - _expressionPrinter.Print(Expression.MakeBinary(ExpressionType.Or, Expression.Constant(7), Expression.Constant(42)))); + _expressionPrinter.PrintExpression(Expression.MakeBinary(ExpressionType.Or, Expression.Constant(7), Expression.Constant(42)))); Assert.Equal( "7 ^ 42", - _expressionPrinter.Print( + _expressionPrinter.PrintExpression( Expression.MakeBinary(ExpressionType.ExclusiveOr, Expression.Constant(7), Expression.Constant(42)))); Assert.Equal( "7 + 42", - _expressionPrinter.Print(Expression.MakeBinary(ExpressionType.Add, Expression.Constant(7), Expression.Constant(42)))); + _expressionPrinter.PrintExpression(Expression.MakeBinary(ExpressionType.Add, Expression.Constant(7), Expression.Constant(42)))); Assert.Equal( "7 - 42", - _expressionPrinter.Print(Expression.MakeBinary(ExpressionType.Subtract, Expression.Constant(7), Expression.Constant(42)))); + _expressionPrinter.PrintExpression(Expression.MakeBinary(ExpressionType.Subtract, Expression.Constant(7), Expression.Constant(42)))); Assert.Equal( "7 * 42", - _expressionPrinter.Print(Expression.MakeBinary(ExpressionType.Multiply, Expression.Constant(7), Expression.Constant(42)))); + _expressionPrinter.PrintExpression(Expression.MakeBinary(ExpressionType.Multiply, Expression.Constant(7), Expression.Constant(42)))); Assert.Equal( "7 / 42", - _expressionPrinter.Print(Expression.MakeBinary(ExpressionType.Divide, Expression.Constant(7), Expression.Constant(42)))); + _expressionPrinter.PrintExpression(Expression.MakeBinary(ExpressionType.Divide, Expression.Constant(7), Expression.Constant(42)))); Assert.Equal( "7 % 42", - _expressionPrinter.Print(Expression.MakeBinary(ExpressionType.Modulo, Expression.Constant(7), Expression.Constant(42)))); + _expressionPrinter.PrintExpression(Expression.MakeBinary(ExpressionType.Modulo, Expression.Constant(7), Expression.Constant(42)))); } [ConditionalFact] public void ConditionalExpression_printed_correctly() => Assert.Equal( "True ? \"Foo\" : \"Bar\"", - _expressionPrinter.Print( + _expressionPrinter.PrintExpression( Expression.Condition( Expression.Constant(true), Expression.Constant("Foo"), @@ -99,7 +99,7 @@ public void ConditionalExpression_printed_correctly() public void Simple_lambda_printed_correctly() => Assert.Equal( "prm => 42", - _expressionPrinter.Print( + _expressionPrinter.PrintExpression( Expression.Lambda( Expression.Constant(42), Expression.Parameter(typeof(int), "prm")))); @@ -108,7 +108,7 @@ public void Simple_lambda_printed_correctly() public void Multi_parameter_lambda_printed_correctly() => Assert.Equal( "(prm1, prm2) => 42", - _expressionPrinter.Print( + _expressionPrinter.PrintExpression( Expression.Lambda( Expression.Constant(42), Expression.Parameter(typeof(int), "prm1"), @@ -118,7 +118,7 @@ public void Multi_parameter_lambda_printed_correctly() public void Unhandled_parameter_in_lambda_detected() => Assert.Equal( "prm1{0} => (Unhandled parameter: prm2){1}", - _expressionPrinter.PrintDebug( + _expressionPrinter.PrintExpressionDebug( Expression.Lambda( Expression.Parameter(typeof(int), "prm2"), Expression.Parameter(typeof(int), "prm1")))); @@ -127,7 +127,7 @@ public void Unhandled_parameter_in_lambda_detected() public void MemberAccess_after_BinaryExpression_adds_parentheses() => Assert.Equal( @"(7 + 42).Value", - _expressionPrinter.Print( + _expressionPrinter.PrintExpression( Expression.Property( Expression.Add( Expression.Constant(7, typeof(int?)), @@ -138,7 +138,7 @@ public void MemberAccess_after_BinaryExpression_adds_parentheses() public void Simple_MethodCall_printed_correctly() => Assert.Equal( @"""Foo"".ToUpper()", - _expressionPrinter.Print( + _expressionPrinter.PrintExpression( Expression.Call( Expression.Constant("Foo"), typeof(string).GetMethods().Single(m => m.Name == nameof(string.ToUpper) && m.GetParameters().Count() == 0)))); @@ -150,7 +150,7 @@ public void Complex_MethodCall_printed_correctly() + @".Substring( startIndex: 0, length: 4)", - _expressionPrinter.Print( + _expressionPrinter.PrintExpression( Expression.Call( Expression.Constant("Foobar"), typeof(string).GetMethods().Single(m => m.Name == nameof(string.Substring) && m.GetParameters().Count() == 2), @@ -175,7 +175,7 @@ public void Linq_methods_printed_as_extensions() .Select(x => x.ToString()) .AsEnumerable() .Where(x => x.Length > 1)", - _expressionPrinter.Print(expr.Body), + _expressionPrinter.PrintExpression(expr.Body), ignoreLineEndingDifferences: true); } @@ -183,7 +183,7 @@ public void Linq_methods_printed_as_extensions() public void Enumerable_Constant_printed_correctly() => Assert.Equal( @"int[] { 1, 2, 3 }", - _expressionPrinter.Print( + _expressionPrinter.PrintExpression( Expression.Constant( new[] { 1, 2, 3 }))); }