dotnet / dotnet/efcore

EF can't translate the query

Open
#33,313 1 comment 0 reactions 0 assignees View on GitHub
area-groupby area-query customer-reported
Dominant language
C#
Stars
14.8k
Forks
3.4k
PR merge metrics
PR metrics pending

Description

EF Core version: 8.0.1
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 8

I have a `query`:

```
var query = db
.Processes(queryTenantId)
.SelectMany(
a => db.ProcessesActions.Where(aa => a.TenantId != null && a.ProcessId == aa.ProcessId && a.TenantId == aa.TenantId),
(a, aa) => new
{
Process = a,
Action = aa
}
)
.GroupBy(r => new
{
r.Process.ProcessId,
r.Process.Name,
r.Process.ProcessType,
r.Process.Description,
r.Process.Permission,
r.Process.TenantId
})
.Select(
r => new MyQueryResult
{
ProcessId = r.Key.ProcessId,
Name = r.Key.Name,
ProcessType = (ProcessType)r.Key.ProcessType,
Description = (ProcessDescription)r.Key.Description,
Permission = (ProcessPermission)r.Key.Permission,
ActionCount = r.Sum(rr => rr.Action.ActionCount)
}
)
;
```

The code above results in the following SQL query:

```
DECLARE @__tenantId_1 int = 123;

SELECT [g].[ProcessId], [g].[Name], CAST([g].[ProcessType] AS int) AS [ProcessType], CAST([g].[Description] AS int) AS [Description], CAST([g].[Permission] AS int) AS [Permission], COALESCE(SUM([f].[ActionCount]), 0) AS [ActionCount]
FROM [dbo].[Processes](@__tenantId_1) AS [g]
INNER JOIN [ProcessesActions] AS [f] ON [g].[ProcessId] = [f].[ProcessId] AND [g].[TenantId] = [f].[TenantId]
GROUP BY [g].[ProcessId], [g].[Name], [g].[ProcessType], [g].[Description], [g].[Permission], [g].[TenantId]
```

Now I want to get only couple of columns based on the query above:

```
var query1 = query
.Select(r => new
{
r.ProcessId,
r.Name
})
;
```

`query1` translates in the following SQL query:

```
DECLARE @__tenantId_1 int = 123;

SELECT [g].[ProcessId], [g].[Name]
FROM [dbo].[Processes](@__tenantId_1) AS [g]
INNER JOIN [ProcessesActions] AS [f] ON [g].[ProcessId] = [f].[ProcessId] AND [g].[TenantId] = [f].[TenantId]
GROUP BY [g].[ProcessId], [g].[Name], [g].[ProcessType], [g].[Description], [g].[Permission], [g].[TenantId]
```

Exactly what I want. Now I need to modify the `query` and make it like this:

```
var query = db
.Processes(queryTenantId)
.SelectMany(
a => db.ProcessesActions.Where(aa => a.TenantId != null && a.ProcessId == aa.ProcessId && a.TenantId == aa.TenantId),
(a, aa) => new
{
Process = a,
Action = aa
}
)
.GroupBy(r => new
{
r.Process.ProcessId,
r.Process.Name,
r.Process.ProcessType,
r.Process.Description,
r.Process.Permission,
r.Process.TenantId
})
.Select(
r => new MyQueryResult
{
ProcessId = r.Key.ProcessId,
Name = r.Key.Name,
ProcessType = (ProcessType)r.Key.ProcessType,
Description = (ProcessDescription)r.Key.Description,
Permission = (ProcessPermission)r.Key.Permission,
ActionCount = r.Sum(rr => rr.Action.ActionCount),
Extensions = db.ProcessesActions
.Where(rr => r.Key.ProcessId == rr.ProcessId && r.Key.TenantId == rr.TenantId)
.GroupBy(r => new
{
r.Extension
})
.Select(r => new ProcessRuleWithExtentionsExtensionResponseItem
{
Extension = r.Key.Extension,
ActionCount = r.Sum(rr => rr.ActionCount)
})
.ToList()
}
)
;
```

I added Extensions property. After changes above, the `query` translates to the following SQL query:

```
DECLARE @__tenantId_1 int = 123;

SELECT [t].[ProcessId], [t].[Name], [t].[c], [t].[c0], [t].[c1], [t].[c2], [t].[ProcessType], [t].[Description], [t].[Permission], [t].[TenantId], [t0].[Extension], [t0].[ActionCount]
FROM (
SELECT [g].[ProcessId], [g].[Name], CAST([g].[ProcessType] AS int) AS [c], CAST([g].[Description] AS int) AS [c0], CAST([g].[Permission] AS int) AS [c1], COALESCE(SUM([f].[ActionCount]), 0) AS [c2], [g].[ProcessType], [g].[Description], [g].[Permission], [g].[TenantId]
FROM [dbo].[Processes](@__tenantId_1) AS [g]
INNER JOIN [ProcessesActions] AS [f] ON [g].[ProcessId] = [f].[ProcessId] AND [g].[TenantId] = [f].[TenantId]
GROUP BY [g].[ProcessId], [g].[Name], [g].[ProcessType], [g].[Description], [g].[Permission], [g].[TenantId]
) AS [t]
OUTER APPLY (
SELECT [f0].[Extension], COALESCE(SUM([f0].[ActionCount]), 0) AS [ActionCount]
FROM [ProcessesActions] AS [f0]
WHERE [t].[ProcessId] = [f0].[ProcessId] AND [t].[TenantId] = [f0].[TenantId]
GROUP BY [f0].[Extension]
) AS [t0]
ORDER BY [t].[ProcessId], [t].[Name], [t].[ProcessType], [t].[Description], [t].[Permission], [t].[TenantId]
```

After the last changes of the `query`, the `query1` can't be translated to SQL query. I get the following error when I try to execute it:

> Nullable object must have a value.

Here is the Expression of the `query1`:

```
MyDbContext.Processes(<>c__DisplayClass559_0.tenantId)
.SelectMany(
collectionSelector: a => <>c__DisplayClass2_0.db.ProcessesActions
.Where(aa => a.TenantId != null && a.ProcessId == aa.ProcessId && a.TenantId == (int?)aa.TenantId),
resultSelector: (a, aa) => new {
Process = a,
Action = aa
})
.GroupBy(r => new {
ProcessId = r.Process.ProcessId,
Name = r.Process.Name,
ProcessType = r.Process.ProcessType,
Description = r.Process.Description,
Permission = r.Process.Permission,
TenantId = r.Process.TenantId
})
.Select(r => new MyQueryResult{
ProcessId = r.Key.ProcessId,
Name = r.Key.Name,
ProcessType = (ProcessType)r.Key.ProcessType,
Description = (ProcessDescription)r.Key.Description,
Permission = (ProcessPermission)r.Key.Permission,
ActionCount = r
.Sum(rr => rr.Action.ActionCount),
Extensions = <>c__DisplayClass2_0.db.ProcessesActions
.Where(rr => r.Key.ProcessId == rr.ProcessId && r.Key.TenantId == (int?)rr.TenantId)
.GroupBy(r => new { Extension = r.Extension })
.Select(r => new ProcessRuleWithExtentionsExtensionResponseItem{
Extension = r.Key.Extension,
ActionCount = r
.Sum(rr => rr.ActionCount)
}
)
.ToList()
}
)
.Select(r => new {
ProcessId = r.ProcessId,
Name = r.Name
})
```

How to fix the issue?

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.