dotnet/efcore · error · InvalidOperationException

'DefaultIfEmpty' cannot be applied after a client-evaluated…

Error message

'DefaultIfEmpty' cannot be applied after a client-evaluated projection. Consider applying 'DefaultIfEmpty' before last 'Select' or use 'AsEnumerable' before 'DefaultIfEmpty' to apply it on client-side.

What it means

InMemoryQueryExpression.ApplyDefaultIfEmpty checks whether any client projections have been accumulated (_clientProjections.Count != 0). If so, it cannot inject a null-padding DefaultIfEmpty at the server ValueBuffer level because the client projection layer has already reshaped the stream, so it throws DefaultIfEmptyAppliedAfterProjection. The provider needs DefaultIfEmpty to operate on the raw server projection before any client-side Select reshaping.

Solutions

  1. Apply DefaultIfEmpty before the last Select call so the provider can pad ValueBuffers at the server level.
  2. Insert AsEnumerable() before DefaultIfEmpty to perform the default-if-empty logic on the client side.
  3. Restructure the left-join so the DefaultIfEmpty sits directly on the inner sequence before any projection.

Example fix

// before — DefaultIfEmpty after client-projection Select
var query = from o in ctx.Orders
            from p in o.Items.Select(i => new { i.Id, Desc = Format(i.Name) }).DefaultIfEmpty()
            select new { o.Id, p?.Desc };

// after — apply DefaultIfEmpty on the raw inner sequence first
var query = ctx.Orders
    .SelectMany(o => o.Items.AsQueryable().DefaultIfEmpty(), (o, i) => new { o.Id, Desc = i != null ? Format(i.Name) : null })
    .AsEnumerable();
Defensive patterns

Strategy: validation

Validate before calling

// Validate query structure: apply DefaultIfEmpty before any Select that
// triggers client projection. For left-join patterns, put DefaultIfEmpty
// directly on the inner sequence.
// Structural rule: DefaultIfEmpty must see only server-translatable projections.
var query = ctx.Orders
    .SelectMany(o => ctx.Items.Where(i => i.OrderId == o.Id).DefaultIfEmpty(),
               (o, i) => new { Order = o, Item = i });

Try / catch

try
{
    return query.ToList();
}
catch (InvalidOperationException ex) when (ex.Message.Contains("DefaultIfEmpty"))
{
    // Move DefaultIfEmpty before the client Select
    var baseQuery = ctx.Orders.SelectMany(
        o => ctx.Items.Where(i => i.OrderId == o.Id).DefaultIfEmpty(),
        (o, i) => new { o, i });
    return baseQuery.AsEnumerable().Select(projectClient).ToList();
}

Prevention

When it happens

Trigger: A LINQ query that applies DefaultIfEmpty (typically via a left join pattern) after a Select that has already triggered client projection evaluation in the InMemory provider.

Common situations: Simulating left joins with SelectMany + DefaultIfEmpty where the preceding Select includes a non-translatable expression, or composing DefaultIfEmpty after a group/join that already forced client evaluation. Often surfaces when testing InMemory with queries written for SQL providers.

Related errors


AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11). Data as JSON: /api/errors/215f3a2302602e8e. Report an issue: GitHub.

Appendix: source

Thrown at src/EFCore.InMemory/Query/Internal/InMemoryQueryExpression.cs:463

        {
            throw new InvalidOperationException(InMemoryStrings.SetOperationsNotAllowedAfterClientEvaluation);
        }

        ServerQueryExpression = Call(
            setOperationMethodInfo.MakeGenericMethod(typeof(ValueBuffer)), ServerQueryExpression, source2.ServerQueryExpression);
    }

    /// <summary>
    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
    ///     any release. You should only use it directly in your code with extreme caution and knowing that
    ///     doing so can result in application failures when updating to a new Entity Framework Core release.
    /// </summary>
    public virtual void ApplyDefaultIfEmpty()
    {
        if (_clientProjections.Count != 0)
        {
            throw new InvalidOperationException(InMemoryStrings.DefaultIfEmptyAppliedAfterProjection);
        }

        ServerQueryExpression = Call(
            EnumerableMethods.DefaultIfEmptyWithArgument.MakeGenericMethod(typeof(ValueBuffer)),
            ServerQueryExpression,
            Constant(new ValueBuffer(Enumerable.Repeat((object?)null, _projectionMappingExpressions.Count).ToArray())));

        ReplaceProjection(
            _projectionMapping.ToDictionary(
                kv => kv.Key,
                kv => kv.Value switch
                {
                    EntityProjectionExpression p => MakeEntityProjectionNullable(p),

                    var p when !p.Type.IsNullableType()
                        => Coalesce(
                            MakeReadValueNullable(p),
                            p.Type.GetDefaultValueConstant()),

View on GitHub (pinned to 3a2006ef56)