dotnet / dotnet/efcore

How to perform multiple aggregates without a group by clause?

Open
#27,117 5 comments 8 reactions 0 assignees View on GitHub
area-query customer-reported
Dominant language
C#
Stars
14.8k
Forks
3.4k
PR merge metrics
PR metrics pending

Description

Consider the following query:
``` c#
from t in db.Table
group (int?)t.Value by 1 into tg
select new
{
A = tg.Max(),
B = tg.Count()
C = tg.Sum()
}
```

EFC5 translation:
```sql
SELECT MAX(m.Value) AS A, COUNT(m.Value) AS B, COALESCE(SUM(m.Value), 0) AS C
FROM Table AS m
```

EFC6 translation:
```sql
SELECT MAX(t.Value) AS A, COUNT(*) AS B, COALESCE(SUM(m.Value), 0) AS C
FROM
(
SELECT m.Value, 1 AS Key
FROM Table AS m
) AS t
GROUP BY t.Key
```

When the table (i.e. source being aggregated) is empty the EFC5 query returns `(NULL, 0)`, the EFC6 query returns no rows.
Considering that EFC6 behavior matches the in-memory Enumerable behavior I guess this breaking change is intentional. However I would still need a mechanism to aggregate (without a `GROUP BY` clause) that always returns exactly 1 row whether the source is empty or not. How can I express this in LINQ?

This
```c#
from t in db.Table.DefaultIfEmpty()
group (int?)...
```
returns bad data: `(NULL, 1)`

This
```c#
(from t in ...
).DefaultIfEmpty()
```
throws an exception:
```
System.InvalidOperationException: Nullable object must have a value.
at lambda_method97(Closure , QueryContext , DbDataReader , ResultContext , SingleQueryResultCoordinator )
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at Program.Main()
```

### Include your code

```csproj


Exe
net6.0



```
```c#
class Program
{
public class Table
{
public int Id { get; set; }
public int? Value { get; set; }
}
public class TestDb : DbContext
{
public DbSet Table { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("data source=dummy");
}
}
static void Main()
{
using var db = new TestDb();
var q = from t in db.Table
group (int?)t.Value by 1 into tg
select new
{
A = tg.Max(),
B = tg.Count(),
C = tg.Sum(),
};
var items = q.ToList();
}
}
```

### Include provider and version information

EF Core version: 6.0.1
Database provider: Microsoft.EntityFrameworkCore.SqlServer 6.0.1
Target framework: .NET 6.0
Operating system: Windows 10 21H1
IDE: Visual Studio 2022 17.0.4

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.