{"record":{"id":"b8a7374500ef838f","repo":"dotnet/efcore","slug":"selectexpression-can-only-be-built-over-a-jsonquer","errorCode":null,"errorMessage":"SelectExpression can only be built over a JsonQueryExpression that represents a collection within the JSON document.","messagePattern":"SelectExpression can only be built over a JsonQueryExpression that represents a collection within the JSON document\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.CreateSelect.cs","lineNumber":855,"sourceCode":"    }\n\n    /// <summary>\n    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to\n    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in\n    ///     any release. You should only use it directly in your code with extreme caution and knowing that\n    ///     doing so can result in application failures when updating to a new Entity Framework Core release.\n    /// </summary>\n    [EntityFrameworkInternal]\n    protected virtual SelectExpression CreateSelect(\n        JsonQueryExpression jsonQueryExpression,\n        TableExpressionBase tableExpressionBase,\n        string identifierColumnName,\n        Type identifierColumnType,\n        RelationalTypeMapping identifierColumnTypeMapping)\n    {\n        if (!jsonQueryExpression.IsCollection)\n        {\n            throw new ArgumentException(RelationalStrings.SelectCanOnlyBeBuiltOnCollectionJsonQuery, nameof(jsonQueryExpression));\n        }\n\n        var structuralType = jsonQueryExpression.StructuralType;\n        var jsonColumn = jsonQueryExpression.JsonColumn;\n        var tableAlias = tableExpressionBase.Alias!;\n\n        Check.DebugAssert(\n            structuralType.BaseType is null && !structuralType.GetDirectlyDerivedTypes().Any(),\n            \"Inheritance encountered inside a JSON document\");\n\n        // Create a dictionary mapping all properties to their ColumnExpressions, for the SelectExpression's projection.\n        var propertyExpressions = new Dictionary<IProperty, ColumnExpression>();\n        foreach (var property in structuralType.GetPropertiesInHierarchy())\n        {\n            // For owned JSON mapping, add column(s) representing key of the parent (non-JSON) entity, on top of all the projections from OPENJSON/json_each/etc.\n            if (jsonQueryExpression.KeyPropertyMap?.TryGetValue(property, out var ownerKeyColumn) == true)\n            {\n                propertyExpressions[property] = ownerKeyColumn;","sourceCodeStart":837,"sourceCodeEnd":873,"githubUrl":"https://github.com/dotnet/efcore/blob/3a2006ef569de08368d59db5e1468aa8f407e4f8/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.CreateSelect.cs#L837-L873","documentation":"CreateSelect over a JsonQueryExpression requires the JSON entity to be a collection (a JSON array). This ArgumentException fires when the JsonQueryExpression.IsCollection is false, i.e. someone tried to build a SelectExpression over a single (scalar/entity) JSON value. Single-valued JSON owned entities are accessed as columns through their owner, not as independent query roots.","triggerScenarios":"Internal/advanced usage that constructs a JsonQueryExpression for a non-collection JSON owned entity and then tries to build a SelectExpression over it. End-user-reachable through APIs that expose JSON-mapped owned entities as queryable when the owned entity is mapped as a single object (.ToJson() on OwnsOne) rather than a collection (.ToJson() on OwnsMany).","commonSituations":"Calling .AsQueryable() on a reference (non-collection) JSON-mapped owned navigation and trying to compose; third-party extensions that build JSON queries incorrectly; misconfiguring OwnsOne with ToJson and treating the result as a queryable collection.","solutions":["Access a single-valued JSON owned entity through its owner: project owner.Owned.Property rather than querying the owned entity as a root.","If you need to query a collection inside JSON, map it with OwnsMany(...).ToJson(...) so IsCollection is true.","Do not call .AsQueryable()/LINQ operators that turn a single-valued JSON navigation into a SelectExpression root."],"exampleFix":"// before (single-valued JSON owned treated as queryable)\nmodelBuilder.Entity<Order>().OwnsOne(o => o.Address, a => a.ToJson());\nvar q = db.Orders.SelectMany(o => new[] { o.Address }.AsQueryable());\n// after (project through owner)\nvar q = db.Orders.Select(o => new { o.Id, o.Address.City });","handlingStrategy":"validation","validationCode":"// Do not turn a single-valued JSON owned entity into a query root.\n// OwnsOne(...).ToJson() => scalar/entity (not collection); access via the owner.\n// OwnsMany(...).ToJson() => collection; safe to compose.\nvar owneds = db.Model.FindEntityType(typeof(Owner))!\n    .GetNavigations().Select(n => n.TargetEntityType)\n    .Where(t => t.IsOwned() && t.IsMappedToJson());\nforeach (var owned in owneds)\n    if (!owned.FindOwnership()!.IsUnique)\n        Console.WriteLine($\"{owned.Name}: JSON collection (queryable)\");\n    else\n        Console.WriteLine($\"{owned.Name}: JSON scalar/entity (access via owner)\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Access single-valued JSON owned entities via their owner projection.","Reserve .AsQueryable() over JSON for OwnsMany(...).ToJson() collections.","Document which owned navigations are JSON collections vs. single objects."],"tags":["efcore","json","owned-types","select-expression","query-translation"],"backgroundTag":null,"analyzedSha":"3a2006ef569de08368d59db5e1468aa8f407e4f8","analyzedAt":"2026-08-11T23:42:04.146Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}