Entity Framework Core Raw SQL — From FromSql() to Production‑Grade Safety
Most .NET developers eventually hit the same moment with Entity Framework Core:
“LINQ is great… but I need raw SQL here.”
That’s when APIs like FromSqlRaw, ExecuteSqlRaw, and FromSqlInterpolated enter the picture — and that’s also where security, performance, and correctness can silently go wrong.
This article goes deeper than the docs:
- Why EF Core deprecated some APIs
- What really happens inside EF Core, the JIT, and the database engine
- How to use raw SQL safely without losing performance
- Mental models used by senior .NET engineers in production systems
If you’ve ever wondered “what’s the real difference?”, this post is for you.
Table of Contents
- The Mental Model: Why Raw SQL Exists in EF Core
- FromSql vs FromSqlRaw vs FromSqlInterpolated
- Deprecated APIs — What Changed and Why
- What Happens Under the Hood (EF Core + ADO.NET + SQL Server)
- Security Deep Dive: SQL Injection at the Instruction Level
- Performance Reality: Query Plans, Parameterization, and Caching
- ExecuteSqlRaw for Commands (UPDATE / DELETE)
- Advanced Patterns Used in Production
- Common Pitfalls (and How Senior Devs Avoid Them)
- Production Checklist
1. Mental Model: Why Raw SQL Exists in EF Core
EF Core was designed around LINQ-to-Entities, but no ORM can fully replace SQL.
Raw SQL exists for three main reasons:
Expressiveness
Some queries (CTEs, window functions, hints) are awkward or impossible in LINQ.Performance
Hand‑tuned SQL can outperform generated queries in hot paths.Integration
Legacy databases, views, stored procedures, and reporting queries.
Key idea:
EF Core does not bypass ADO.NET.
Raw SQL still flows through parameterization, command preparation, and execution plans.
2. FromSql vs FromSqlRaw vs FromSqlInterpolated
❌ FromSql() (Deprecated)
Historically:
context.Users.FromSql("SELECT * FROM Users WHERE Name = {0}", name);
- Automatically parameterized
- Safer by default
- Removed to reduce ambiguity and API surface
✅ FromSqlRaw()
context.Users
.FromSqlRaw("SELECT * FROM Users WHERE Name = @name",
new SqlParameter("@name", name));
Characteristics:
- Maximum control
- Zero magic
- You are responsible for parameter safety
✅ FromSqlInterpolated() (Recommended)
context.Users
.FromSqlInterpolated($"SELECT * FROM Users WHERE Name = {name}");
EF Core converts interpolation into parameters automatically.
3. Deprecated APIs — What Changed and Why
EF Core removed:
FromSqlExecuteSqlSqlQuery
Reason:
- Too easy to introduce SQL injection
- Ambiguous safety guarantees
- Harder to audit at scale
4. What Happens Under the Hood
Pipeline:
- EF Core builds a relational command
- Parameters become
DbParameter - ADO.NET executes the command
- Database parses, optimizes, executes
- Results stream back
Parameterized SQL enables plan reuse.
5. SQL Injection — Real Mechanics
Injection changes the SQL parse tree.
Bad:
FromSqlRaw($"SELECT * FROM Users WHERE Name = '{input}'");
Good:
FromSqlInterpolated($"SELECT * FROM Users WHERE Name = {input}");
6. Performance Reality
- Parameterized SQL = cached plans
- String SQL = recompilation
- Compilation cost adds latency
Secure SQL is almost always faster SQL.
7. ExecuteSqlRaw for Commands
context.Database.ExecuteSqlRaw(
"UPDATE Users SET IsActive = 0 WHERE LastLogin < @date",
new SqlParameter("@date", cutoff));
8. Advanced Production Patterns
Hybrid LINQ + SQL
var query =
context.Users
.FromSqlInterpolated($"SELECT * FROM Users WHERE IsActive = {true}")
.AsNoTracking()
.Where(u => u.Role == "Admin");
9. Common Pitfalls
- Concatenating user input
- Forgetting
AsNoTracking() - Assuming raw SQL bypasses EF Core
- Ignoring execution plans
10. Production Checklist
- [ ] Parameterized queries only
- [ ] No string concatenation
- [ ] Execution plans reviewed
- [ ] Load tested hot paths
- [ ] Logging enabled
Final Thought
Raw SQL is not an escape hatch.
It’s a precision tool — powerful, fast, and dangerous if misunderstood.
Use it like a scientist, not like a cowboy.

Top comments (0)