Skip to content

Commit

Permalink
Lift try..catch out of an expression and replace with a temporary var…
Browse files Browse the repository at this point in the history
…iable

Fixes dotnet#34393
  • Loading branch information
ChrisJollyAU committed Oct 15, 2024
1 parent a0d6550 commit 29e8218
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 24 deletions.
27 changes: 25 additions & 2 deletions src/EFCore.Design/Query/Internal/LinqToCSharpSyntaxTranslator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2039,6 +2039,28 @@ LiteralExpressionSyntax literal when literal.IsKind(SyntaxKind.NullLiteralExpres
};
}

private Expression LiftTryExpression(TryExpression tryExpression)
{
using var _ = ChangeContext(ExpressionContext.Statement);
var name = UniquifyVariableName("temp");
var variable = Expression.Parameter(tryExpression.Type, name);
var newBody = Expression.Assign(variable, tryExpression.Body);
var newTry = tryExpression.Update(newBody, tryExpression.Handlers, tryExpression.Finally, tryExpression.Fault);

_liftedState.Statements.Add(GenerateVarDeclaration(name, DefaultExpression(Generate(variable.Type))));

var re = Translate<TryStatementSyntax>(newTry);
_liftedState.Statements.Add(re);
_liftedState.VariableNames.Add(name);
_liftedState.Variables[variable] = name;

var currentStack = _stack.Peek();
currentStack.Variables[variable] = name;
currentStack.VariableNames.Add(name);

return variable;
}

/// <inheritdoc />
protected override Expression VisitNewArray(NewArrayExpression newArray)
{
Expand Down Expand Up @@ -2414,8 +2436,9 @@ protected override Expression VisitTry(TryExpression tryNode)

case ExpressionContext.Expression:
case ExpressionContext.ExpressionLambda:
throw new NotImplementedException();

var liftedVariable = LiftTryExpression(tryNode);
Result = Translate(liftedVariable);
return tryNode;
default:
throw new ArgumentOutOfRangeException();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,16 +126,17 @@ public static TValue ThrowReadValueException<TValue>(
throw new InvalidOperationException(message, exception);
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static TValue ThrowExtractJsonPropertyException<TValue>(Exception exception, IProperty property)
{
var entityType = property.DeclaringType.DisplayName();
var propertyName = property.Name;

throw new InvalidOperationException(
[EntityFrameworkInternal]
public static TValue ThrowExtractJsonPropertyException<TValue>(Exception exception, string entityType, string propertyName) => throw new InvalidOperationException(
RelationalStrings.JsonErrorExtractingJsonProperty(entityType, propertyName),
exception);
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2863,20 +2863,33 @@ Expression valueExpression
&& !buffering)
{
var exceptionParameter = Parameter(typeof(Exception), name: "e");

var catchBlock = Catch(
exceptionParameter,
Call(
ThrowReadValueExceptionMethod.MakeGenericMethod(valueExpression.Type),
CatchBlock? catchBlock = null;
if (property != null)
{
catchBlock = Catch(
exceptionParameter,
Call(dbDataReader, GetFieldValueMethod.MakeGenericMethod(typeof(object)), indexExpression),
Constant(valueExpression.Type.MakeNullable(nullable), typeof(Type)),
_parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
property,
LiftableConstantExpressionHelpers.BuildMemberAccessLambdaForProperty(property),
property + "Property",
typeof(IPropertyBase))));

Call(
ThrowReadValueExceptionMethod.MakeGenericMethod(valueExpression.Type),
exceptionParameter,
Call(dbDataReader, GetFieldValueMethod.MakeGenericMethod(typeof(object)), indexExpression),
Constant(valueExpression.Type.MakeNullable(nullable)),
_parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
property,
LiftableConstantExpressionHelpers.BuildMemberAccessLambdaForProperty(property),
property.Name + "Property",
typeof(IPropertyBase))));
}
else
{
catchBlock = Catch(
exceptionParameter,
Call(
ThrowReadValueExceptionMethod.MakeGenericMethod(valueExpression.Type),
exceptionParameter,
Call(dbDataReader, GetFieldValueMethod.MakeGenericMethod(typeof(object)), indexExpression),
Constant(valueExpression.Type.MakeNullable(nullable)),
Constant(null,typeof(IPropertyBase))));
}
valueExpression = TryCatch(valueExpression, catchBlock);
}

Expand Down Expand Up @@ -3005,7 +3018,8 @@ private Expression CreateReadJsonPropertyValueExpression(
Call(
ThrowExtractJsonPropertyExceptionMethod.MakeGenericMethod(resultExpression.Type),
exceptionParameter,
Constant(property, typeof(IProperty))));
Constant(property.DeclaringType.DisplayName(), typeof(string)),
Constant(property.Name, typeof(string))));

resultExpression = TryCatch(resultExpression, catchBlock);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2091,7 +2091,7 @@ public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder build
// TODO: Figure out if there's a nice way to continue using the retrying strategy
var sqlServerOptionsBuilder = new SqlServerDbContextOptionsBuilder(builder);
sqlServerOptionsBuilder.ExecutionStrategy(d => new NonRetryingExecutionStrategy(d));
return builder;
return builder.EnableDetailedErrors();
}

public override PrecompiledQueryTestHelpers PrecompiledQueryTestHelpers
Expand Down

0 comments on commit 29e8218

Please sign in to comment.