dotnet/efcore · error · InvalidOperationException

SqlQueryUnmappedType

SqlQueryUnmappedType

Error message

The element type '{elementType}' used in 'SqlQuery' method is not natively supported by your database provider. Either use a supported element type, or use ModelConfigurationBuilder.DefaultTypeMapping to define a mapping for your type.

What it means

In the SqlQueryRootExpression case (line 215-251), EF looks up a relational type mapping for the element type via the type mapping source. Raw SQL queries (SqlQueryRaw/SqlQuery<T>) require each element to be a type the provider can map to a single column. If FindMapping returns null, SqlQueryUnmappedType is thrown, suggesting DefaultTypeMapping configuration.

Source

Thrown at src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs:222

                Check.DebugAssert(
                    !subquerySourceSelectExpression.HasNonEntityNullabilityMarkers,
                    "Non-grouping subquery clone carries a live non-entity nullability marker; it would be stranded by Clone(). "
                    + "Route it through a marker-aware remap as the GroupByShaperExpression case does.");

                var clonedSelectExpression = subquerySourceSelectExpression.Clone();
                return new ShapedQueryExpression(
                    clonedSelectExpression,
                    new QueryExpressionReplacingExpressionVisitor(shapedQueryExpression.QueryExpression, clonedSelectExpression)
                        .Visit(shapedQueryExpression.ShaperExpression));

            case SqlQueryRootExpression sqlQueryRootExpression:
            {
                var typeMapping = RelationalDependencies.TypeMappingSource.FindMapping(
                    sqlQueryRootExpression.ElementType, RelationalDependencies.Model);

                if (typeMapping == null)
                {
                    throw new InvalidOperationException(
                        RelationalStrings.SqlQueryUnmappedType(sqlQueryRootExpression.ElementType.DisplayName()));
                }

                var alias = _sqlAliasManager.GenerateTableAlias("sql");
                var selectExpression = new SelectExpression(
                    [new FromSqlExpression(alias, sqlQueryRootExpression.Sql, sqlQueryRootExpression.Argument)],
                    new ColumnExpression(
                        SqlQuerySingleColumnAlias,
                        alias,
                        sqlQueryRootExpression.Type.UnwrapNullableType(),
                        typeMapping,
                        sqlQueryRootExpression.Type.IsNullableType()),
                    identifier: [],
                    _sqlAliasManager);

                Expression shaperExpression = new ProjectionBindingExpression(
                    selectExpression, new ProjectionMember(), sqlQueryRootExpression.ElementType.MakeNullable());

View on GitHub (pinned to dbf9771522)

Solutions

  1. Use a natively supported primitive type (int, long, string, Guid, DateTime, decimal, etc.) as the element type.
  2. Register a mapping for your custom type via ModelConfigurationBuilder.DefaultTypeMapping<T>() in OnModelCreating/Configure.
  3. Map the result to an entity type and query the DbSet instead, or project scalar columns separately.

Example fix

// before (custom struct has no mapping)
var ids = db.Database.SqlQueryRaw<MyCustomId>("SELECT id FROM t");

// after (use a supported primitive, or register a mapping)
var ids = db.Database.SqlQueryRaw<int>("SELECT id FROM t");
// or, in model configuration:
// configurationBuilder.DefaultTypeMapping<MyCustomId>();
Defensive patterns

Strategy: validation

Validate before calling

// Confirm the element type has a relational mapping before calling SqlQuery<T>.
var mapping = db.GetService<IRelationalTypeMappingSource>().FindMapping(typeof(T));
if (mapping is null) throw new InvalidOperationException($"No mapping for {typeof(T)}; use a primitive or register DefaultTypeMapping.");

Type guard

// Restrict SqlQuery element types to provider-supported primitives at compile time.
static bool IsSupportedElementType(Type t) =>
    t == typeof(int) || t == typeof(long) || t == typeof(string)
    || t == typeof(Guid) || t == typeof(DateTime) || t == typeof(decimal)
    || t == typeof(double) || t == typeof(bool);

Try / catch

try { var rows = await db.Database.SqlQueryRaw<T>(sql).ToListAsync(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("not natively supported"))
{ /* switch T to a primitive, or register DefaultTypeMapping<T> */ }

Prevention

When it happens

Trigger: Calling context.Database.SqlQueryRaw<T>(sql) or SqlQuery<T>(sql) where T is not a natively supported primitive (int, string, DateTime, etc.) and has no configured mapping — e.g. a custom struct, a complex object, or an unmapped enum.

Common situations: Using SqlQuery<T> with a custom value type or DTO that the provider does not know how to read from a single result column; forgetting that SqlQuery<T> maps T to one column (unlike entity queries); provider that lacks a mapping for an uncommon primitive (e.g. some numeric/decimal variants).

Related errors


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