Skip to content

Commit 55d9ce4

Browse files
authored
Merge pull request #67 from EFNext/fix/avg-window-nullable-fix
fix AVG over nullable int?/long? window
2 parents a781ea0 + 6af1c35 commit 55d9ce4

2 files changed

Lines changed: 27 additions & 2 deletions

File tree

src/ExpressiveSharp.EntityFrameworkCore.RelationalExtensions/Infrastructure/Internal/WindowFunctionMethodCallTranslator.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ [new OrderingExpression(
8181
// cast the argument so SQL computes a floating-point AVG, not integer division.
8282
[NeedsFloatCast(expr, method.ReturnType)
8383
? _sqlExpressionFactory.ApplyDefaultTypeMapping(
84-
_sqlExpressionFactory.Convert(expr, method.ReturnType))
84+
_sqlExpressionFactory.Convert(expr, typeof(double)))
8585
: expr],
8686
spec, method.ReturnType),
8787

@@ -209,7 +209,7 @@ private WindowFunctionSqlExpression MakeNavigation(
209209
// SQL Server performs integer division for AVG(int) — cast the argument when the
210210
// CLR return type is floating-point but the expression is an integer type.
211211
private static bool NeedsFloatCast(SqlExpression expr, Type returnType) =>
212-
returnType == typeof(double)
212+
(returnType == typeof(double) || returnType == typeof(double?))
213213
&& expr.Type is var t
214214
&& (t == typeof(int) || t == typeof(long) || t == typeof(int?) || t == typeof(long?));
215215

tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Infrastructure/WindowFunctionTestBase.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,31 @@ public async Task Average_IntColumn_ReturnsDouble()
798798
Assert.AreEqual(2.1, results[^1].AvgQty, 0.01);
799799
}
800800

801+
[TestMethod]
802+
public async Task Average_NullableIntColumn_CastsToFloat()
803+
{
804+
var query = Context.Orders
805+
.Select(o => new
806+
{
807+
o.Id,
808+
AvgCustomer = WindowFunction.Average(o.CustomerId,
809+
Window.OrderBy(o.Id)
810+
.RowsBetween(WindowFrameBound.UnboundedPreceding, WindowFrameBound.UnboundedFollowing)),
811+
})
812+
.OrderBy(x => x.Id);
813+
814+
var sql = query.ToQueryString();
815+
StringAssert.Contains(sql, "AVG");
816+
// Float cast renders per-provider: CAST(... AS ...) on most, ::double precision on PostgreSQL.
817+
Assert.IsTrue(sql.Contains("CAST(") || sql.Contains("::"),
818+
$"AVG argument should be cast to floating point, but no cast was found in: {sql}");
819+
820+
var results = await query.ToListAsync();
821+
Assert.AreEqual(10, results.Count);
822+
// AVG of CustomerId (five 1s + five 2s = 15/10) must be 1.5, not integer-divided to 1.
823+
Assert.AreEqual(1.5, results[0].AvgCustomer!.Value, 0.01);
824+
}
825+
801826
[TestMethod]
802827
public async Task Sum_WithoutFrame_UsesDefaultFrame()
803828
{

0 commit comments

Comments
 (0)