查看ORM (EFCore)生成 SQL 的方法:

通用定位 ORM 生成 SQL 的方法可以总结成一条链路:

接口请求 -> TraceId -> EF Core SQL 日志 -> TagWith 标签 -> 慢 SQL / 表名 / 参数 -> SSMS 执行计划

推荐做法

  1. 本地开发先看 EF Core 控制台日志
    调接口后直接看控制台输出的 SQL,最快确认 ORM 到底生成了什么。

  2. 日志多时先按TraceId / RequestId过滤
    一次 HTTP 请求会有一个请求标识,用它把“这个接口”和“它执行的 SQL”关联起来。

  3. 给关键 LINQ 加TagWith()
    例如:

var rows = await db.Orders .TagWith("Orders.ProjectedList") .AsNoTracking() .Where(o => o.Status == status) .OrderByDescending(o => o.OrderDate) .Skip((pageIndex - 1) * pageSize) .Take(pageSize) .Select(o => new OrderListItemDto( o.OrderId, o.OrderNo, o.Customer.Name, o.TotalAmount, o.Status, o.OrderDate)) .ToListAsync();

我这边添加这一行:

日志里的 SQL 会带:

-- Orders.ProjectedList SELECT ...

以后直接搜:

Orders.ProjectedList

就能准确定位对应 SQL。

所以日志很多时,你就搜:

Orders.ProjectedList

找到它之后,从标签这一行开始,到这条 SQL 结束,就是对应的 ORM SQL。

你这段可以提取成 SSMS 可执行版本:

SET STATISTICS IO ON; SET STATISTICS TIME ON; DECLARE @__status_0 nvarchar(20) = N'Paid'; DECLARE @__p_1 int = 0; DECLARE @__p_2 int = 10; -- Orders.ProjectedList SELECT [t].[OrderId], [t].[OrderNo], [c].[Name], [t].[TotalAmount], [t].[Status], [t].[OrderDate] FROM ( SELECT [o].[OrderId], [o].[CustomerId], [o].[OrderDate], [o].[OrderNo], [o].[Status], [o].[TotalAmount] FROM [Orders] AS [o] WHERE [o].[Status] = @__status_0 ORDER BY [o].[OrderDate] DESC OFFSET @__p_1 ROWS FETCH NEXT @__p_2 ROWS ONLY ) AS [t] INNER JOIN [Customers] AS [c] ON [t].[CustomerId] = [c].[CustomerId] ORDER BY [t].[OrderDate] DESC;

怎么提取

  1. 找日志里的Executed DbCommand
  2. Parameters=[...],把参数转成DECLARE
  3. Orders.ProjectedList
  4. 从:
-- Orders.ProjectedList

开始复制 5. 一直复制到本条 SQL 的最后一行 6. 放到 SSMS,前面补:

SET STATISTICS IO ON; SET STATISTICS TIME ON;

这段日志怎么读

Executed DbCommand (10ms)

数据库执行耗时约10ms

@__status_0='Paid' @__p_1='0' @__p_2='10'

参数值分别是:

状态 = Paid 跳过 = 0 取 10 条
-- Orders.ProjectedList

这是 ORM 查询标签,用来定位是哪段 LINQ。

FROM [Orders] AS [o] WHERE [o].[Status] = @__status_0 ORDER BY [o].[OrderDate] DESC OFFSET @__p_1 ROWS FETCH NEXT @__p_2 ROWS ONLY

说明分页在 SQL Server 里完成。

INNER JOIN [Customers] AS [c]

说明为了拿客户名,关联了客户表。

SELECT [t].[OrderId], [t].[OrderNo], [c].[Name], ...

说明这是 DTO 投影查询,只查需要的列,不是SELECT *

一句话总结

日志多时,先搜 TagWith 标签,比如 Orders.ProjectedList。 找到标签后,从标签开始复制 SQL。 再把 Parameters 转成 DECLARE,放到 SSMS 看执行计划。

4. 再用表名、参数、耗时缩小范围
比如搜:

Orders Customers @__status_0='Paid' Executed DbCommand (214ms)

5. 找到 SQL 后复制到 SSMS
补上参数DECLARE,打开实际执行计划和STATISTICS IO/TIME分析。

大型项目标准组合

TraceId / RequestId + EF Core Database.Command 日志 + TagWith 查询标签 + 慢 SQL 日志 + Query Store

一句话:

小项目靠控制台和表名定位;大项目靠 TraceId 串请求、TagWith 标查询、慢 SQL 找重点、Query Store 看历史表现。