You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的核心差异

  1. 非实体查询的API入口不同

    • EF6直接用context.Database.SqlQuery<MyDto>("SQL")即可;
    • EF Core 3.x-6.x需要通过context.Set<MyDto>().FromSqlRaw(),EF7+才回归类似EF6的Database.SqlQuery<T>写法。
  2. LINQ后续操作的执行位置不同

    • EF6中,SqlQuery之后的LINQ操作(比如Where、OrderBy)都是在客户端内存中执行的;
    • EF Core中,FromSqlRaw/FromSqlInterpolated之后的LINQ操作会被EF Core解析为SQL,追加到原始查询中,在数据库端执行,性能更好。
  3. 参数化查询的安全性设计

    • EF6支持直接传参数组,但也允许字符串拼接(风险高);
    • EF Core强制推荐FromSqlInterpolated(自动参数化)或FromSqlRaw的占位符方式,从API设计上就引导开发者避免SQL注入,安全性更强。
  4. 类型映射的严格性

    • EF6对非实体类型的构造函数、属性映射要求比较宽松;
    • EF Core要求非实体类必须有公共无参构造函数,属性和列名的映射规则更严格,必须完全匹配或通过特性/别名指定。
  5. 跟踪行为

    • EF6中SqlQuery返回的对象默认是不被上下文跟踪的;
    • EF Core中,通过Set<T>().FromSqlRaw()查询的非实体对象也不会被跟踪(因为EF Core只跟踪实体类型),这点和EF6一致,但如果是实体类型的查询,默认是跟踪的。

内容的提问来源于stack exchange,提问作者serenity

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 10:23:38