dotnet/efcore · error · InvalidOperationException
ExecuteOperationWithUnsupportedOperatorInSqlGeneration
ExecuteOperationWithUnsupportedOperatorInSqlGeneration
Error message
The operation '{operation}' contains a select expression feature that isn't supported in the query SQL generator, but has been declared as supported by provider during translation phase. This is a bug in your EF Core provider, file an issue at https://aka.ms/efcorefeedback. What it means
Thrown by QuerySqlGenerator.VisitDelete (QuerySqlGenerator.cs:231) when an ExecuteDelete (bulk delete) operation's SelectExpression contains a feature the SQL generator cannot render as a simple DELETE (e.g. multiple tables, GROUP BY/HAVING, OFFSET/LIMIT, ordering, or projections). The translation phase marked the operation as supported, but SQL generation finds it isn't, which the message calls out as a provider bug. (The error text references ExecuteDelete specifically.)
Source
Thrown at src/EFCore.Relational/Query/QuerySqlGenerator.cs:231
Orderings: [],
Offset: null,
Limit: null
}
&& table.Equals(deleteExpression.Table))
{
_relationalCommandBuilder.Append("DELETE FROM ");
Visit(deleteExpression.Table);
if (selectExpression.Predicate != null)
{
_relationalCommandBuilder.AppendLine().Append("WHERE ");
Visit(selectExpression.Predicate);
}
return deleteExpression;
}
throw new InvalidOperationException(
RelationalStrings.ExecuteOperationWithUnsupportedOperatorInSqlGeneration(
nameof(EntityFrameworkQueryableExtensions.ExecuteDelete)));
}
/// <summary>
/// Generates SQL for a SELECT expression.
/// </summary>
/// <param name="selectExpression">The <see cref="SelectExpression" /> for which to generate SQL.</param>
protected virtual Expression VisitSelect(SelectExpression selectExpression)
{
IDisposable? subQueryIndent = null;
if (selectExpression.Alias != null)
{
_relationalCommandBuilder.AppendLine("(");
subQueryIndent = _relationalCommandBuilder.Indent();
}
if (!TryGenerateWithoutWrappingSelect(selectExpression))View on GitHub (pinned to dbf9771522)
Solutions
- Rewrite ExecuteDelete to target a single table with a simple predicate; precompute the keys to delete and delete by primary key.
- Move filtering into a simple Where on the entity's own columns so the SelectExpression reduces to one table + WHERE.
- If a JOIN is needed, first select the keys into a list, then ExecuteDelete on keys (e.g. Where(e => keys.Contains(e.Id))).
- If you maintain a provider, extend VisitDelete (or restrict translation) so declared support matches generatable SQL; report via the feedback link.
Example fix
// before - ExecuteDelete over a join/group cannot be rendered
await ctx.Orders
.Where(o => o.Customer.Region == "EU") // forces a JOIN -> unsupported
.ExecuteDeleteAsync();
// after - reduce to a single-table predicate, or delete by precomputed keys
var ids = ctx.Orders
.Where(o => o.Customer.Region == "EU")
.Select(o => o.Id).ToList();
await ctx.Orders.Where(o => ids.Contains(o.Id)).ExecuteDeleteAsync(); Defensive patterns
Strategy: validation
Validate before calling
// Reduce ExecuteDelete to a single-table predicate before calling it. // If filtering requires related data, precompute keys and delete by key. var ids = ctx.Orders.Where(o => o.Customer.Region == "EU").Select(o => o.Id).ToList(); await ctx.Orders.Where(o => ids.Contains(o.Id)).ExecuteDeleteAsync();
Try / catch
try { await ctx.Orders.Where(predicate).ExecuteDeleteAsync(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("select expression feature that isn't supported")) {
// ExecuteDelete can't render this select; delete by precomputed keys instead.
var ids = ctx.Orders.Where(predicate).Select(o => o.Id).ToList();
await ctx.Orders.Where(o => ids.Contains(o.Id)).ExecuteDeleteAsync();
} Prevention
- Use ExecuteDelete only against a single table with a simple WHERE on its own columns.
- When filtering needs related data, precompute keys and delete by primary key.
- Avoid ExecuteDelete over joins, groupings, distinct, or limit clauses.
- Provider authors: keep declared translation support in sync with generatable SQL.
When it happens
Trigger: Calling ExecuteDelete on a query whose translated SelectExpression is more complex than a single-table DELETE with an optional WHERE (e.g. deleting via a JOIN, after grouping, with a limit, or with projections). The translation layer accepted it, but VisitDelete only handles {single table, no group/having/projection/order/offset/limit}.
Common situations: ExecuteDelete over a query with Include/Join (multi-table), GroupBy, Distinct, Skip/Take, or a subquery that produces a non-simple select; using ExecuteDelete where the predicate requires joining; provider declaring more support than it can generate.
Related errors
- Unhandled expression '{expression}' of type '{expressionType
- UnhandledExpressionInVisitor
- InvalidFromSqlArguments
- Cosmos-specific methods can only be used when the context is
- Cosmos SQL does not allow Offset without Limit. Consider spe
AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06).
Data as JSON: /api/errors/fc8747274a91fd8f.
Report an issue: GitHub.