LINQ is incredibly powerful, but it cannot express every possible SQL feature. If you have an ultra-complex SQL Server Stored Procedure written by a 30-year DBA veteran, or a heavily optimized Common Table Expression (CTE), you don't rewrite it in C#. You drop down into Raw SQL using EF Core.
EF Core allows you to execute SQL string literals directly, mapping the results back to your strongly-typed C# Entities.
// Danger: FromSqlRaw is vulnerable to SQL Injection if you manually concatenate strings!
var rawQuery = await _context.Products
.FromSqlRaw("SELECT * FROM Products WHERE CategoryId = {0}", categoryId)
.ToListAsync();
// Safer: FromSqlInterpolated uses C# string interpolation syntax ($"...")
// but parses it securely into SQL Parameters automatically!
var safeQuery = await _context.Products
.FromSqlInterpolated($"SELECT * FROM Products WHERE CategoryId = {categoryId}")
.ToListAsync();
This is the most powerful feature of FromSqlRaw. If you execute a Stored Procedure or complex SQL view, EF Core treats the result as an IQueryable. That means you can dynamically append C# LINQ commands on top of the raw SQL!
// 1. Execute a massive SQL View
var query = _context.Products
.FromSqlRaw("SELECT * FROM vw_ComplexProductReporting");
// 2. Append standard LINQ paging dynamically!
// EF Core wraps your raw query dynamically:
// SELECT * FROM (SELECT * FROM vw...) ORDER BY Name OFFSET 50 ROWS FETCH NEXT 10 ROWS ONLY
var pagedResults = await query
.OrderBy(p => p.Name)
.Skip(50)
.Take(10)
.ToListAsync();
EF Core's FromSqlRaw has a strict limitation: The SQL you execute MUST return columns that map perfectly to an existing DbSet<Entity> that EF Core is tracking. You cannot use EF Core to execute a random SQL query like SELECT AVG(Price) as Average From Products if you don't have a C# class with those exact properties mapped in the DbContext.
SqlConnection, use Dapper's .QueryAsync<T>(), and benefit from nanosecond response times.
Q: "We used `.FromSqlInterpolated` to execute a stored procedure that returns our `User` objects. Later in the method, we modified the `user.Name` and called `SaveChanges()`. Will the database update?"
Architect Answer: "Yes! This is the fundamental difference between EF Core Raw SQL and a micro-ORM like Dapper. Even though you used a raw SQL string to retrieve the data from the database, because the result was mapped to a tracked `DbSet