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
22 changes: 19 additions & 3 deletions src/EFCore.Design/Query/Internal/CSharpToLinqTranslator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -777,9 +777,25 @@ IEnumerable<KeyValuePair<string, Type>> GetTypeTypeParameters(INamedTypeSymbol t
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public override Expression VisitLiteralExpression(LiteralExpressionSyntax literal)
=> _semanticModel.GetTypeInfo(literal) is { ConvertedType: { } type }
? Constant(literal.Token.Value, ResolveType(type))
: Constant(literal.Token.Value);
{
var value = literal.Token.Value;
if (_semanticModel.GetTypeInfo(literal) is not { ConvertedType: { } type })
{
return Constant(value);
}

var clrType = ResolveType(type);
if (value is not null
&& value.GetType() != clrType.UnwrapNullableType()
&& value.GetType().IsNumeric()
&& clrType.IsNumeric())
{
value = System.Convert.ChangeType(
value is char c ? (int)c : value, clrType.UnwrapNullableType(), CultureInfo.InvariantCulture);
}

return Constant(value, clrType);
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down
33 changes: 33 additions & 0 deletions test/EFCore.Design.Tests/Query/CSharpToLinqTranslatorTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,39 @@ public void Literal_decimal()
() => 1.5m,
"1.5m");

[Theory]
[InlineData("(decimal amount) => amount > 0 ? amount : 0")]
[InlineData("(decimal amount) => amount > 0m ? amount : 0")]
[InlineData("(decimal amount) => amount > 0m ? amount : 0m")]
public void Literal_implicitly_converted_to_decimal(string code)
=> AssertExpression(
() => new[] { 1m }.Select(amount => amount > 0m ? amount : 0m),
$"new[] {{ 1m }}.Select({code})");

[Fact]
public void Literal_char_implicitly_converted_to_decimal()
=> AssertExpression(
() => new decimal[] { 'a' },
"new decimal[] { 'a' }");

[Fact]
public void Literal_implicitly_converted_to_byte()
=> AssertExpression(
() => new byte[] { 255 },
"new byte[] { 255 }");

[Fact]
public void Literal_implicitly_converted_to_long()
=> AssertExpression(
() => new long[] { 1 },
"new long[] { 1 }");

[Fact]
public void Literal_implicitly_converted_to_double()
=> AssertExpression(
() => new double[] { 1 },
"new double[] { 1 }");

[Fact]
public void Literal_null()
=> AssertExpression(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,40 @@ public AdHocPrecompiledQueryRelationalTestBase(NonSharedFixture fixture, ITestOu
: base(fixture)
=> TestOutputHelper = testOutputHelper;

[Fact]
public virtual async Task Decimal_projection_with_implicitly_converted_zero()
{
var contextFactory = await InitializeNonSharedTest<DecimalContext>();

await Test(
"""
await using var context = new AdHocPrecompiledQueryRelationalTestBase.DecimalContext(dbContextOptions);
var id = 1;
var amount = 2m;

// Generate and compile the query without executing it.
Func<Task<decimal>> query = () => context.Groups.Where(group => group.Id == id)
.Select(group => group.Balance + (amount > 0 ? amount : 0))
.SingleAsync();
""",
typeof(DecimalContext),
contextFactory.GetOptions(),
interceptorCodeAsserter: code => Assert.Contains("InterceptsLocation", code));
}

public class DecimalContext(DbContextOptions options) : DbContext(options)
{
public DbSet<DecimalGroup> Groups { get; set; } = null!;
}

public class DecimalGroup
{
public int Id { get; set; }

[Precision(18, 2)]
public decimal Balance { get; set; }
}

[Fact]
public virtual async Task Index_no_evaluatability()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ protected override DbContextOptionsBuilder UseWindowFunctionAggregates(DbContext
return optionsBuilder;
}

public override async Task Decimal_projection_with_implicitly_converted_zero()
{
await base.Decimal_projection_with_implicitly_converted_zero();

AssertSql();
}

public override async Task GroupBy_elements_and_aggregates_as_window_functions()
{
await base.GroupBy_elements_and_aggregates_as_window_functions();
Expand Down
Loading