Entity Framework 6与EF Core原生SQL:非实体类型SQL查询实现方法
在Entity Framework Core中运行非实体类型的SQL查询
好问题!从EF6转到EF Core时,很多开发者都会纠结非实体类型的原生SQL查询怎么写,我来给你拆解清楚:
一、EF Core中编写非实体类型SQL查询的方法
EF Core针对非实体查询的API在不同版本有一些变化,分两种场景来说:
1. 映射到自定义DTO(数据传输对象)
首先你需要定义一个和查询结果结构匹配的类,比如我们要查商品销售统计:
public class ProductSalesDto { public int ProductId { get; set; } public string ProductName { get; set; } public int TotalSales { get; set; } }
然后根据你的EF Core版本选择对应的写法:
EF Core 3.0 ~ 6.x版本
用Set<T>()结合FromSqlRaw或FromSqlInterpolated:
// 方式1:使用FromSqlRaw(手动传参,防止注入) var categoryId = 5; var salesData = context.Set<ProductSalesDto>() .FromSqlRaw("SELECT ProductId, ProductName, TotalSales FROM ProductSales WHERE CategoryId = {0}", categoryId) .ToList(); // 方式2:使用FromSqlInterpolated(更简洁,自动参数化) var salesData = context.Set<ProductSalesDto>() .FromSqlInterpolated($"SELECT ProductId, ProductName, TotalSales FROM ProductSales WHERE CategoryId = {categoryId}") .ToList();
注意:DTO类需要有公共无参构造函数,属性名要和SQL查询的列名完全匹配(大小写不敏感,但最好一致),如果列名不一样,可以用SQL别名或者
[Column]特性来映射。
EF Core 7.0+版本
官方新增了Database.SqlQuery<T>()方法,和EF6的写法更接近,不用再通过Set<T>():
var salesData = context.Database.SqlQuery<ProductSalesDto>( "SELECT ProductId, ProductName, TotalSales FROM ProductSales WHERE CategoryId = @categoryId", new SqlParameter("@categoryId", categoryId) ).ToList();
2. 查询单个值/零散数据(无需DTO)
如果只是查单个数值、字符串等,比如统计总销售额:
- EF Core 7.0+可以直接用
SqlQuery<T>:
var totalRevenue = context.Database.SqlQuery<decimal>("SELECT SUM(Revenue) FROM ProductSales").FirstOrDefault();
- 更早版本可以用
Set<T>().FromSqlRaw():
var totalRevenue = context.Set<decimal>() .FromSqlRaw("SELECT SUM(Revenue) FROM ProductSales") .FirstOrDefault();
二、EF6和EF Core原生SQL的核心差异
非实体查询的API入口不同
- EF6直接用
context.Database.SqlQuery<MyDto>("SQL")即可; - EF Core 3.x-6.x需要通过
context.Set<MyDto>().FromSqlRaw(),EF7+才回归类似EF6的Database.SqlQuery<T>写法。
- EF6直接用
LINQ后续操作的执行位置不同
- EF6中,
SqlQuery之后的LINQ操作(比如Where、OrderBy)都是在客户端内存中执行的; - EF Core中,
FromSqlRaw/FromSqlInterpolated之后的LINQ操作会被EF Core解析为SQL,追加到原始查询中,在数据库端执行,性能更好。
- EF6中,
参数化查询的安全性设计
- EF6支持直接传参数组,但也允许字符串拼接(风险高);
- EF Core强制推荐
FromSqlInterpolated(自动参数化)或FromSqlRaw的占位符方式,从API设计上就引导开发者避免SQL注入,安全性更强。
类型映射的严格性
- EF6对非实体类型的构造函数、属性映射要求比较宽松;
- EF Core要求非实体类必须有公共无参构造函数,属性和列名的映射规则更严格,必须完全匹配或通过特性/别名指定。
跟踪行为
- EF6中
SqlQuery返回的对象默认是不被上下文跟踪的; - EF Core中,通过
Set<T>().FromSqlRaw()查询的非实体对象也不会被跟踪(因为EF Core只跟踪实体类型),这点和EF6一致,但如果是实体类型的查询,默认是跟踪的。
- EF6中
内容的提问来源于stack exchange,提问作者serenity
相关产品推荐
相关产品推荐

