Bug description
LinqToCSharpSyntaxTranslator turns EF's shaper expression trees into C# for precompiled queries. Block variables and lambda parameters keep the names they were given when the expression tree was built. The translator uniquifies those names (UniquifyVariableName) but does not check that they are valid identifiers, and emits them directly (VariableDeclarator(Identifier(name))).
Expression tree variable names are arbitrary strings. In the relational shaper some are derived from model configuration: the JSON materializer names runtime constants after JSON property names (already sanitized by PrecompiledQueryCodeGenerator.SanitizeIdentifierName, see Invalid_identifier_json_property_name), and any block variable named the same way is emitted unsanitized. During review of #39014, a block variable named <jsonPropertyName>Read for a complex collection whose JSON name was 1!NOT VALID COLLECTION; produced:
Test.EFInterceptors.InvalidNameContext.g.cs(127,26): error CS1001: Identifier expected
That PR avoided the problem by leaving the variable unnamed, but the translator remains one named variable away from the same failure.
Expected behavior
Any variable or parameter name the translator emits is a valid C# identifier, the same way runtime constant names already are (1!NOT VALID; becomes _1_NOT_VALID_).
Proposed fix
Move the identifier sanitizer from PrecompiledQueryCodeGenerator into LinqToCSharpSyntaxTranslator as a shared method and apply it in UniquifyVariableName, which every emitted variable and lambda parameter name already passes through. The generator calls the shared method for runtime constants. One translator unit test:
var i = Parameter(typeof(int), "1!not valid;");
AssertStatement(Block(variables: [i], Assign(i, Constant(3))), """
{
var _1_not_valid_ = 3;
}
""");
Bug description
LinqToCSharpSyntaxTranslatorturns EF's shaper expression trees into C# for precompiled queries. Block variables and lambda parameters keep the names they were given when the expression tree was built. The translator uniquifies those names (UniquifyVariableName) but does not check that they are valid identifiers, and emits them directly (VariableDeclarator(Identifier(name))).Expression tree variable names are arbitrary strings. In the relational shaper some are derived from model configuration: the JSON materializer names runtime constants after JSON property names (already sanitized by
PrecompiledQueryCodeGenerator.SanitizeIdentifierName, seeInvalid_identifier_json_property_name), and any block variable named the same way is emitted unsanitized. During review of #39014, a block variable named<jsonPropertyName>Readfor a complex collection whose JSON name was1!NOT VALID COLLECTION;produced:That PR avoided the problem by leaving the variable unnamed, but the translator remains one named variable away from the same failure.
Expected behavior
Any variable or parameter name the translator emits is a valid C# identifier, the same way runtime constant names already are (
1!NOT VALID;becomes_1_NOT_VALID_).Proposed fix
Move the identifier sanitizer from
PrecompiledQueryCodeGeneratorintoLinqToCSharpSyntaxTranslatoras a shared method and apply it inUniquifyVariableName, which every emitted variable and lambda parameter name already passes through. The generator calls the shared method for runtime constants. One translator unit test: