Raw SQL Queries without DbSet in Entity Framework Core

Nov 21, 2025 · Programming · 12 views · 7.8

Keywords: Entity Framework Core | Raw SQL Queries | Keyless Entity Types | Parameterized Queries | Full-Text Search

Abstract: This comprehensive technical article explores various methods for executing raw SQL queries in Entity Framework Core that do not map to existing DbSets. It covers the evolution from query types in EF Core 2.1 to the SqlQuery method in EF Core 8.0, providing complete code examples for configuring keyless entity types, executing queries with computed fields, and handling parameterized query security. The article compares compatibility differences across EF Core versions and offers practical guidance for selecting appropriate solutions in real-world projects.

Problem Background and Challenges

Throughout Entity Framework Core's development, developers frequently encounter scenarios requiring raw SQL queries, particularly when query results include computed fields (such as full-text search rankings) that cannot be directly mapped to existing entity models. The removal of the traditional dbData.Database.SqlQuery<SomeModel> method in EF Core has necessitated alternative approaches for handling these complex query requirements.

EF Core 8.0 and Newer Solutions

Starting with EF Core 8.0, Microsoft introduced support for raw SQL queries returning unmapped types. The SqlQuery method can now return arbitrary types, not just scalar values. This enables developers to directly execute SQL queries returning complex objects (including computed fields) without pre-defining entity mappings.

// EF Core 8.0+ Example
var results = await context.Database
    .SqlQuery<SearchResult>("SELECT Name, RANK() OVER (ORDER BY Score DESC) AS Rank FROM Products")
    .ToListAsync();

public class SearchResult
{
    public string Name { get; set; }
    public int Rank { get; set; }
}

EF Core 3.0-7.0: Keyless Entity Types

For EF Core versions 3.0 through 7.0, keyless entity types are recommended for handling such scenarios. Keyless entity types were known as query types in EF Core 2.1 and were renamed and enhanced in version 3.0.

Configuring Keyless Entity Types

First, configure entity classes as keyless types using data annotations or fluent configuration:

// Using Data Annotations
[Keyless]
public class ProductSearchResult
{
    public int ProductId { get; set; }
    public string ProductName { get; set; }
    public decimal SearchRank { get; set; }
}

// Or configure in DbContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<ProductSearchResult>().HasNoKey();
}

// Register DbSet
public DbSet<ProductSearchResult> ProductSearchResults { get; set; }

Executing Raw SQL Queries

After configuration, use FromSqlRaw or FromSql methods to execute queries:

// Synchronous version
var results = context.ProductSearchResults
    .FromSqlRaw("SELECT ProductId, Name AS ProductName, RANK() OVER (ORDER BY Relevance) AS SearchRank FROM Products WHERE CONTAINS(Name, @p0)", searchTerm)
    .ToList();

// Asynchronous version
var results = await context.ProductSearchResults
    .FromSqlInterpolated($"SELECT ProductId, Name AS ProductName, RANK() OVER (ORDER BY Relevance) AS SearchRank FROM Products WHERE CONTAINS(Name, {searchTerm})")
    .ToListAsync();

EF Core 2.1: Query Types Approach

For projects still using EF Core 2.1, query types provide similar functionality:

// Define DbQuery in DbContext
public DbQuery<ProductSearchResult> ProductSearchResults { get; set; }

// Execute query
var results = context.ProductSearchResults
    .FromSql("SELECT ProductId, Name AS ProductName, RANK() OVER (ORDER BY Relevance) AS SearchRank FROM Products")
    .ToList();

Parameterized Queries and Security

Parameterization is crucial for preventing SQL injection attacks when executing raw SQL queries. EF Core provides secure parameterization mechanisms:

// Secure parameterized query
var searchTerm = "electronics";
var minRank = 0.5;

var results = await context.ProductSearchResults
    .FromSqlInterpolated($"SELECT * FROM dbo.SearchProducts({searchTerm}) WHERE Rank > {minRank}")
    .ToListAsync();

// Using named parameters
var searchParam = new SqlParameter("searchTerm", "electronics");
var rankParam = new SqlParameter("minRank", 0.5);

var results = await context.ProductSearchResults
    .FromSqlRaw("SELECT * FROM dbo.SearchProducts(@searchTerm) WHERE Rank > @minRank", searchParam, rankParam)
    .ToListAsync();

Dynamic SQL and FromSqlRaw Usage

In scenarios requiring dynamic SQL construction, use FromSqlRaw with careful attention to security:

// Dynamic column name query (handle with caution)
var columnName = GetSafeColumnName(userInput); // Must validate and sanitize input
var value = new SqlParameter("value", searchValue);

var results = await context.ProductSearchResults
    .FromSqlRaw($"SELECT * FROM Products WHERE {columnName} = @value", value)
    .ToListAsync();

Combining with LINQ

Raw SQL queries can be combined with LINQ operators, with EF Core treating the SQL as a subquery:

var searchTerm = "laptop";
var results = await context.ProductSearchResults
    .FromSqlInterpolated($"SELECT * FROM dbo.SearchProducts({searchTerm})")
    .Where(r => r.SearchRank > 0.8)
    .OrderByDescending(r => r.SearchRank)
    .Take(10)
    .ToListAsync();

Performance Considerations and Best Practices

When using raw SQL queries, consider the following performance factors:

// Read-only query optimization
var results = await context.ProductSearchResults
    .FromSqlInterpolated($"SELECT * FROM dbo.SearchProducts({searchTerm})")
    .AsNoTracking()
    .Where(r => r.SearchRank > 0.5)
    .Skip(0)
    .Take(20)
    .ToListAsync();

Version Compatibility Summary

Raw SQL query support across different EF Core versions:

By selecting the appropriate solution for your current EF Core version, developers can effectively handle complex SQL queries with computed fields while maintaining code security and performance.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.