如何修复 Entity Framework Core 2.0 中的 n+1 问题?
Posted
技术标签:
【中文标题】如何修复 Entity Framework Core 2.0 中的 n+1 问题?【英文标题】:How to fix n+1 problem in Entity Framework Core 2.0? 【发布时间】:2020-04-08 13:34:38 【问题描述】:我有这个代码
var query = DbContext.Set<WorkCertificate>()
.Include(u => u.WorkCertificateWorkers)
.Include(u => u.WorkCertificateGVSs)
.Where(filterExpression)
.Skip(filter.GetSkip()).Take(filter.GetTake());
var list = await query.ToListAsync();
Typebuilder
的 WorkCertificate
。 WorkCertificate
继承自基类。
builder.HasMany(workCertificate => workCertificate.WorkCertificateWorkers)
.WithOne(wcw => wcw.WorkCertificate)
.HasForeignKey(wcw => wcw.WorkCertificateId);
builder.HasMany(workCertificate => workCertificate.WorkCertificateGVSs)
.WithOne(wcw => wcw.WorkCertificate)
.HasForeignKey(wcw => wcw.WorkCertificateId);
此代码生成 SQL。您可以看到有很多与 DB 的连接,这会导致很大的性能问题。
2020-04-08 16:27:52.0918|INFO|SimpleConsoleLogger|Executed DbCommand (59ms) [Parameters=[@__filter_Data_Id_0='?', @__p_2='?', @__p_1='?'], CommandType='Text', CommandTimeout='30']
SELECT "u"."id", "u"."allowed_to_copy", "u"."approval_state", "u"."certificate_category", "u"."xmin", "u"."created_date", "u"."creator_id", "u"."deleted", "u"."global_id", "u"."gvs_analysis_required", "u"."location_detail", "u"."modified_date", "u"."original_number", "u"."previous_approval_state", "u"."status_key", "u"."work_type", "u"."discriminator", "u"."is_expired_message_sent", "u"."isolation_certificate_required", "u"."linked_isolation_certificate_type", "u"."linked_lockout_certificate_type", "u"."lockout_certificate_required", "u"."shift", "u"."work_description_detail"
FROM "public"."certificate" AS "u"
WHERE ("u"."discriminator" = 'workCertificate') AND (("u"."deleted" = FALSE) AND ("u"."id" = @__filter_Data_Id_0))
ORDER BY "u"."id"
LIMIT @__p_2 OFFSET @__p_1
2020-04-08 16:27:52.2706|INFO|SimpleConsoleLogger|Executed DbCommand (80ms) [Parameters=[@__filter_Data_Id_0='?', @__p_2='?', @__p_1='?'], CommandType='Text', CommandTimeout='30']
SELECT "u.WorkCertificateWorkers"."id", "u.WorkCertificateWorkers"."additional_info", "u.WorkCertificateWorkers"."certificate_state", "u.WorkCertificateWorkers"."created_date", "u.WorkCertificateWorkers"."name", "u.WorkCertificateWorkers"."occupation", "u.WorkCertificateWorkers"."position_id", "u.WorkCertificateWorkers"."position_title", "u.WorkCertificateWorkers"."profile_id", "u.WorkCertificateWorkers"."qualification", "u.WorkCertificateWorkers"."work_certificate_id"
FROM "public"."work_certificate_workers" AS "u.WorkCertificateWorkers"
INNER JOIN (
SELECT "u0"."id"
FROM "public"."certificate" AS "u0"
WHERE ("u0"."discriminator" = 'workCertificate') AND (("u0"."deleted" = FALSE) AND ("u0"."id" = @__filter_Data_Id_0))
ORDER BY "u0"."id"
LIMIT @__p_2 OFFSET @__p_1
) AS "t" ON "u.WorkCertificateWorkers"."work_certificate_id" = "t"."id"
ORDER BY "t"."id"
2020-04-08 16:27:52.3493|INFO|SimpleConsoleLogger|Executed DbCommand (50ms) [Parameters=[@__filter_Data_Id_0='?', @__p_2='?', @__p_1='?'], CommandType='Text', CommandTimeout='30']
SELECT "u.WorkCertificateGVSs"."id", "u.WorkCertificateGVSs"."checked_date", "u.WorkCertificateGVSs"."checked_place", "u.WorkCertificateGVSs"."comment", "u.WorkCertificateGVSs"."max_allowed_concentration", "u.WorkCertificateGVSs"."measuring_tool_number", "u.WorkCertificateGVSs"."measuring_tool_verification_date", "u.WorkCertificateGVSs"."name", "u.WorkCertificateGVSs"."position_title", "u.WorkCertificateGVSs"."result", "u.WorkCertificateGVSs"."substance", "u.WorkCertificateGVSs"."type_key", "u.WorkCertificateGVSs"."unit_of_measure", "u.WorkCertificateGVSs"."work_certificate_id"
FROM "public"."work_certificate_gvs" AS "u.WorkCertificateGVSs"
INNER JOIN (
SELECT "u1"."id"
FROM "public"."certificate" AS "u1"
WHERE ("u1"."discriminator" = 'workCertificate') AND (("u1"."deleted" = FALSE) AND ("u1"."id" = @__filter_Data_Id_0))
ORDER BY "u1"."id"
LIMIT @__p_2 OFFSET @__p_1
) AS "t0" ON "u.WorkCertificateGVSs"."work_certificate_id" = "t0"."id"
ORDER BY "t0"."id"
我怎样才能得到单个 SQL 查询而不是我现在得到的?
【问题讨论】:
编写您自己的查询? docs.microsoft.com/en-us/ef/core/querying/raw-sql 谢谢!这是一个变体,但我想使用 linq。 Entity Framework 是 80% 的解决方案,一直如此。对于另外 20%,您仍然需要 SQL。虽然您可能会成功地使用 Linq 使 EF 符合您的意愿,但编写自定义查询可能是一种更好的解决方案,可以让您更好地控制结果。 @SergiiVeremiyenko 你能分享你的数据模型并根据你的问题描述过滤表达式吗? @Nazim 写了什么,但无论如何我都会假设 - 你最终可能会使用 join 加上一些延迟加载(例如,如果你有逆属性等) 【参考方案1】:我通过升级到 ef core 3.1 解决了这个问题
【讨论】:
以上是关于如何修复 Entity Framework Core 2.0 中的 n+1 问题?的主要内容,如果未能解决你的问题,请参考以下文章
Entity Framework Core 数据查询原理详解
Entity Framework 7 支持批量操作和 JSON 列
Entity Framework 学习系列 - 认识理解Entity Framework