dotnet/efcore · error · InvalidOperationException
ExecuteOperationOnKeylessEntityTypeWithUnsupportedOperator
ExecuteOperationOnKeylessEntityTypeWithUnsupportedOperator
Error message
The operation '{operation}' cannot be performed on keyless entity type '{entityType}', since it contains an operator not natively supported by the database provider. What it means
EF Core's ExecuteUpdate usually renders a single UPDATE...WHERE. When the query contains an operator the provider cannot natively express inside an UPDATE (join, group by, distinct, ordering, limit/offset), EF performs a 'pushdown': it wraps the query as a subquery and INNER JOINs it back to the target table on the primary key columns (see PushdownWithPkInnerJoinPredicate at line 62). A keyless entity type (HasNoKey, a view, or a raw SQL root) has no primary key, so the join predicate cannot be built and this error is thrown at line 96-102.
Source
Thrown at src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs:98
if (!IsMemberAccess(
RemapLambdaBody(source, firstPropertySelector).UnwrapTypeConversion(out _),
RelationalDependencies.Model,
out var baseExpression)
|| _sqlTranslator.TranslateProjection(baseExpression) is not StructuralTypeShaperExpression shaper)
{
throw new InvalidOperationException(RelationalStrings.InvalidPropertyInSetProperty(firstPropertySelector));
}
// TODO: #36336
if (shaper.StructuralType is not IEntityType entityType)
{
throw new InvalidOperationException(
RelationalStrings.ExecuteUpdateSubqueryNotSupportedOverComplexTypes(shaper.StructuralType.DisplayName()));
}
if (entityType.FindPrimaryKey() is not { } pk)
{
throw new InvalidOperationException(
RelationalStrings.ExecuteOperationOnKeylessEntityTypeWithUnsupportedOperator(
nameof(EntityFrameworkQueryableExtensions.ExecuteUpdate),
entityType.DisplayName()));
}
// Generate the INNER JOIN around the original query, on the PK properties.
var outer = (ShapedQueryExpression)Visit(new EntityQueryRootExpression(entityType));
var inner = source;
var outerParameter = Expression.Parameter(entityType.ClrType);
var outerKeySelector = Expression.Lambda(outerParameter.CreateKeyValuesExpression(pk.Properties), outerParameter);
var firstPropertyLambdaExpression = setters[0].PropertySelector;
var entitySource = GetEntitySource(RelationalDependencies.Model, firstPropertyLambdaExpression.Body);
var innerKeySelector = Expression.Lambda(
entitySource.CreateKeyValuesExpression(pk.Properties), firstPropertyLambdaExpression.Parameters);
var joinPredicate = CreateJoinPredicate(outer, outerKeySelector, inner, innerKeySelector);
Check.DebugAssert(joinPredicate != null, "Join predicate shouldn't be null");View on GitHub (pinned to dbf9771522)
Solutions
- Remove the operator that forces pushdown (OrderBy, Take, Skip, Distinct, GroupBy) so the provider renders a native UPDATE over the keyless type.
- Define a primary key on the entity type (HasKey in OnModelCreating or by convention) so the pushdown INNER JOIN has join columns.
- If the entity is backed by a view, map it to a real mutable table (ToTable) instead of ToView.
- Fall back to loading the entities and using SaveChanges, or issue the update via raw SQL (FromSql/ExecuteSqlRaw).
Example fix
// before
db.OrderSummaries.HasNoKey().ToView("OrderSummaries")
.OrderBy(v => v.Total).Take(100)
.ExecuteUpdate(s => s.SetProperty(v => v.Archived, true));
// after (drop pushdown-forcing operator so native UPDATE is used)
db.OrderSummaries
.Where(v => v.Archived == false)
.ExecuteUpdate(s => s.SetProperty(v => v.Archived, true)); Defensive patterns
Strategy: validation
Validate before calling
// Before ExecuteUpdate, confirm the entity has a key and the query has no pushdown-forcing operator.
var entityType = db.Model.FindEntityType(typeof(MyView))!;
bool keyless = entityType.FindPrimaryKey() is null;
bool hasPushdownOperator = /* inspect your query chain for OrderBy/Take/Skip/Distinct/GroupBy/Join */;
if (keyless && hasPushdownOperator)
throw new InvalidOperationException("ExecuteUpdate will fail: keyless entity + pushdown operator."); Try / catch
try { await query.ExecuteUpdateAsync(s => ...); }
catch (InvalidOperationException ex) when (ex.Message.Contains("keyless entity type"))
{ /* fall back to SaveChanges or raw SQL, or remove the pushdown operator */ } Prevention
- Do not mark updatable entities with HasNoKey; reserve keyless types for true read-only views.
- Avoid OrderBy/Distinct/GroupBy/Take/Skip immediately before ExecuteUpdate.
- If you must update a view-backed type, map it ToTable instead of ToView.
When it happens
Trigger: Calling ExecuteUpdate over a keyless entity type (HasNoKey or ToView-only) combined with any operator that makes IsValidSelectExpressionForExecuteUpdate return false: OrderBy, Take/Skip (Limit/Offset), Distinct, GroupBy, Having, a multi-table FROM that is not a liftable INNER/CROSS JOIN. The error surfaces only when pushdown is required, not for trivial predicates.
Common situations: Bulk-updating a read model mapped with HasNoKey() or ToView(); chaining .OrderBy(...).Take(...).ExecuteUpdate(...) on a view-backed entity; querying through FromSql/TVFs whose shape is keyless and then updating; migrating a SaveChanges loop to ExecuteUpdate on an entity that was modeled keyless for query-only use.
Related errors
- InvalidValueInSetProperty
- The 'EF.MultipleParameters<T>' method may only be used withi
- ExecuteOperationOnKeylessEntityTypeWithUnsupportedOperator
- ExecuteUpdateSubqueryNotSupportedOverComplexTypes
- ExecuteOperationOnOwnedJsonIsNotSupported
AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06).
Data as JSON: /api/errors/a9dd7f1fbae52a19.
Report an issue: GitHub.