Skip to content
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
43 changes: 33 additions & 10 deletions src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2080,20 +2080,22 @@ bool PreserveConvertNode(Expression expression)

if (evaluateAsParameter)
{
parameterName = tempParameterName ?? "p";

var compilerPrefixIndex = parameterName.LastIndexOf('>');
if (compilerPrefixIndex != -1)
{
parameterName = parameterName[(compilerPrefixIndex + 1)..];
}
parameterName = string.IsNullOrWhiteSpace(tempParameterName) ? "p" : tempParameterName;

// The VB compiler prefixes closure member names with $VB$Local_, remove that (#33150)
if (parameterName.StartsWith("$VB$Local_", StringComparison.Ordinal))
{
parameterName = parameterName.Substring("$VB$Local_".Length);
}

// In many databases, parameter names must start with a letter or underscore.
// The same is true for C# variable names, from which we derive the parameter name, so in principle we shouldn't see an issue;
// but just in case, prepend an underscore if the parameter name doesn't start with a letter or underscore.
if (!char.IsLetter(parameterName[0]) && parameterName[0] != '_')
{
parameterName = "_" + parameterName;
}
Comment thread
roji marked this conversation as resolved.

parameterName = Uniquifier.Uniquify(parameterName, _parameterNames, maxLength: int.MaxValue, uniquifier: _parameterNames.Count);

_parameterNames.Add(parameterName);
Expand Down Expand Up @@ -2147,11 +2149,13 @@ static Expression RemoveConvert(Expression expression)
switch (memberExpression.Member)
{
case FieldInfo fieldInfo:
parameterName = parameterName is null ? fieldInfo.Name : $"{parameterName}_{fieldInfo.Name}";
var name = SanitizeCompilerGeneratedName(fieldInfo.Name);
parameterName = parameterName is null ? name : $"{parameterName}_{name}";
return fieldInfo.GetValue(instanceValue);

case PropertyInfo propertyInfo:
parameterName = parameterName is null ? propertyInfo.Name : $"{parameterName}_{propertyInfo.Name}";
name = SanitizeCompilerGeneratedName(propertyInfo.Name);
parameterName = parameterName is null ? name : $"{parameterName}_{name}";
return propertyInfo.GetValue(instanceValue);
}
}
Expand All @@ -2166,7 +2170,7 @@ static Expression RemoveConvert(Expression expression)
return constantExpression.Value;

case MethodCallExpression methodCallExpression:
parameterName = methodCallExpression.Method.Name;
parameterName = SanitizeCompilerGeneratedName(methodCallExpression.Method.Name);
break;

case UnaryExpression { NodeType: ExpressionType.Convert or ExpressionType.ConvertChecked } unaryExpression
Expand All @@ -2190,6 +2194,25 @@ static Expression RemoveConvert(Expression expression)
exception);
}
}

static string SanitizeCompilerGeneratedName(string s)
{
// Compiler-generated field names intentionally contain illegal characters, specifically angle brackets <>.
// In cases where there's something within the angle brackets, that tends to be the original user-provided variable name
// (e.g. <PropertyName>k__BackingField). If we see angle brackets, extract that out, or it the angle brackets contain no
// content, strip them out entirely and take what comes after.
var closingBracket = s.IndexOf('>');
if (closingBracket == -1)
{
return s;
}

var openingBracket = s.IndexOf('<');

return openingBracket != -1 && openingBracket < closingBracket - 1
? s[(openingBracket + 1)..closingBracket]
: s[(closingBracket + 1)..];
}
}

private Expression ConvertIfNeeded(Expression expression, Type type)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2031,6 +2031,18 @@ FROM root c
SELECT VALUE c
FROM root c
WHERE (c["$type"] = "Order")
""");
});

public override Task Captured_variable_from_switch_case_pattern_matching(bool async)
=> Fixture.NoSyncTest(
async, async a =>
{
await base.Captured_variable_from_switch_case_pattern_matching(a);

AssertSql(
"""
ReadItem(None, ALFKI)
""");
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2993,6 +2993,23 @@ public virtual Task Parameter_extraction_can_throw_exception_from_user_code_2(bo
&& o.OrderDate.Value.Year == dateFilter.Value.Year)));
}

[ConditionalTheory, MemberData(nameof(IsAsyncData))]
public virtual async Task Captured_variable_from_switch_case_pattern_matching(bool async)
{
object customerIdAsObject = "ALFKI";

switch (customerIdAsObject)
{
case string customerId:
{
await AssertQuery(
async,
ss => ss.Set<Customer>().Where(c => c.CustomerID == customerId));
break;
}
}
}

[ConditionalTheory, MemberData(nameof(IsAsyncData))]
public virtual Task Subquery_member_pushdown_does_not_change_original_subquery_model(bool async)
=> AssertQuery(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3236,6 +3236,20 @@ FROM [Orders] AS [o]
""");
}

public override async Task Captured_variable_from_switch_case_pattern_matching(bool async)
{
await base.Captured_variable_from_switch_case_pattern_matching(async);

AssertSql(
"""
@customerId='ALFKI' (Size = 5) (DbType = StringFixedLength)

SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
FROM [Customers] AS [c]
WHERE [c].[CustomerID] = @customerId
""");
}

public override async Task Subquery_member_pushdown_does_not_change_original_subquery_model(bool async)
{
await base.Subquery_member_pushdown_does_not_change_original_subquery_model(async);
Expand Down