EF Core 执行原生 SQL 查询主要使用 FromSqlRaw 和 FromSqlInterpolated,仅适用于 DbSet 且返回对应实体类型;非查询操作用 ExecuteSqlRaw,复杂结果需配合 SqlQueryRaw 或 ADO.NET。

EF Core 执行原生 SQL 查询主要靠 FromSqlRaw(以及安全增强版 FromSqlInterpolated),它适用于需要精细控制 SQL、调用存储过程、或处理复杂查询但 LINQ 难以表达的场景。
FromSqlRaw 基本用法(需配合 DbSet)
它只能用于 DbSet,且返回类型必须是该 DbSet 对应的实体类型(或其可映射的基类/接口)。不能直接返回匿名对象或自定义 DTO —— 若需灵活结果,请改用 Database.ExecuteSqlRaw 或 Database.GetDbConnection() 配合 ADO.NET。
- 写法示例:
var blogs = context.Blogs.FromSqlRaw("SELECT * FROM Blogs WHERE Id > {0}", 10).ToList(); - 注意:SQL 字符串必须以
SELECT开头,EF Core 才能将其映射为实体集合;若执行 INSERT/UPDATE/DELETE,请用ExecuteSqlRaw - 不支持多语句(如
"SELECT ...; SELECT ..."),也不支持非查询语句混在FromSqlRaw中
防止 SQL 注入:优先用 FromSqlInterpolated
FromSqlInterpolated 是带插值语法的安全替代方案,自动参数化所有 $"" 内的变量,避免手拼字符串出错。
- 正确写法:
var minId = 5;
var blogs = context.Blogs.FromSqlInterpolated($"SELECT * FROM Blogs WHERE Id >= {minId}").ToList(); - 变量会被转为命名参数(如
@p0),底层走 ADO.NET 参数化,彻底规避注入风险 - 只接受单个插值字符串,不支持拼接多个字符串再传入
配合 AsNoTracking 提升只读查询性能
原生 SQL 查询默认仍会跟踪实体状态。如果只是读取展示数据,加上 AsNoTracking() 可显著减少内存和性能开销。
- 推荐组合:
var blogs = context.Blogs
.FromSqlInterpolated($"SELECT * FROM Blogs WHERE CreatedAt > {DateTime.Today.AddDays(-7)}")
.AsNoTracking()
.ToList(); - 尤其适合报表、后台列表、API 只读接口等场景
调用存储过程与复杂结果映射
可以调用存储过程,但返回列名和类型必须与实体属性严格匹配(大小写不敏感,但字段数、名称、可空性要一致)。
- 示例(SQL Server):
var users = context.Users
.FromSqlRaw("EXEC GetActiveUsers @p0", DateTime.Now.AddMonths(-1))
.ToList(); - 若存储过程返回字段与实体不一致,可新建一个匹配的
DbSet(需配置为无键实体或使用查询类型),或改用context.Database.SqlQueryRaw(EF Core 5+)() - 注意:存储过程中不能有临时表、SET 语句干扰结果集结构,否则映射可能失败
基本上就这些。FromSqlRaw / Interpolated 是 EF Core 对接原生能力的关键入口,用对了既保持 ORM 便利性,又不失 SQL 灵活性 —— 关键是分清“查实体”还是“做操作”,别把增删改塞进 FromSqlRaw,也别指望它返回任意结构数据。










