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

  1. Rewrite ExecuteDelete to target a single table with a simple predicate; precompute the keys to delete and delete by primary key.
  2. Move filtering into a simple Where on the entity's own columns so the SelectExpression reduces to one table + WHERE.
  3. If a JOIN is needed, first select the keys into a list, then ExecuteDelete on keys (e.g. Where(e => keys.Contains(e.Id))).
  4. 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

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


AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06). Data as JSON: /api/errors/fc8747274a91fd8f. Report an issue: GitHub.