EF Core 调用具有多个连接的存储过程并映射相关数据

Posted

技术标签:

【中文标题】EF Core 调用具有多个连接的存储过程并映射相关数据【英文标题】:EF Core call stored procedures having multiple joins and map the related data 【发布时间】:2021-11-30 17:10:24 【问题描述】:

如何使用 Entity Framework Core 从 ASP.NET Core Web API 调用包含多个连接的存储过程?

假设有部门 -> 员工 -> 地址表。现在,如果我想获取“每个部门有很多员工,每个员工有很多地址”的所有部门和相关数据,那么我们可以在这三个表之间的存储过程中进行内部连接。但是如何在 Web API 中使用 EF Core 获取分组数据(即嵌套列表)?

【问题讨论】:

这能回答你的问题吗? Raw SQL Mapping Stored Procedure Results to POCO/DTO Using Entity Framework Core 2 存储过程的内容和其中使用的 SQL 构造无关紧要。您只需要创建一个类,其成员与 SQL Server 生成的结果集中的返回类型匹配。 应用程序web api、富客户端、控制台应用程序的类型也无关紧要。 EF 在每种类型的应用程序中的工作方式都相同。 我的自定义模型类包含部门属性,然后是员工列表,& 在员工模型中包含地址列表。问题是我能够获取部门的数据,但员工列表为空。当我看到 SP 的响应时,它返回的是平面数据。但我希望每个部门都有一个员工列表,然后每个员工都有一个地址列表。 SQL 查询的结果总是是“平坦的”——这就是 SQL 所做的——它返回一个由行和列组成的结果集。如果您想要这种分层样式的结果 - 您需要在存储过程中创建 XML 或 JSON,并将单个 XML 或 JSON 字符串返回给调用者,然后调用者需要解析此结果并将其重新构建为层次结构数据对象 【参考方案1】:

但是如何在 Web API 中使用 EF Core 获取分组数据(即嵌套列表)?

据我所知,你不能。文档说:

SQL 查询不能包含相关数据。但是,在许多情况下,您可以在查询之上进行撰写

它说

使用 LINQ 组合需要您的原始 SQL 查询是可组合的,因为 EF Core 会将提供的 SQL 视为子查询。 ... SQL Server 不允许组合存储过程调用

总而言之,您已经编写了一个执行SELECT * FROM a. JOIN b ON ... 的存储过程,那么您就没有办法说context.A.FromSqlInterpolated("...") 并让EF 给您一堆A 对象,并填写了所有相关的B 列表。当您运行像context.A.Include(a => a.B) 这样的普通查询时,EF 将知道它所请求的内容,因此它可以将 a.* 列映射到 A 的实例,并将 b.* 列映射到 B 的实例(简单地说,如果 EF 执行 SELECT a.One, a.Two, b.One, b.Two FROM a JOIN b,它可以知道前两列应该映射到 A 实例,后两列应该映射到 B 实例,它可以跟踪它以前见过的 A 并构建相关实体的图)但只是直接给出结果集并告诉映射将它映射到对象图并不是普遍可行的(假设您的 sproc 确实 SELECT a.One, a.Two, b.One, b.Two FROM a JOIN b - 所有 EF 将看到的结果集是“一、二、一、二”的结果集 - 那当然可以映射到 A :B?又是哪一种呢?如果 C 也有一列呢?..)

您可以考虑使用 Dapper;它将能够运行存储过程,获取数据表并使用其拆分功能将其解压缩到每行的 A 和 B(以及 C 等),然后由您构建对象树(dapper 调用一个方法你提供,构建树)..

..但是你会买了 EF 狗并自己吠叫。如果您在任何地方都这样做过(使用充满连接的存储过程),那么使用 EF 似乎没什么意义..

您也许可以采用类似的“自己动手”方法,创建一个具有 A、B 和 C 所具有的所有属性的实体,然后让 EF 填写它的集合,然后对其进行后期处理查找所有重复的 A 和 B 并构建它们的字典以重建图;但是你正在重建 EF 所做的事情,这也回到了“使用 EF 没有什么意义”

【讨论】:

使用 EF,一旦响应到达,如果我使用 groupby deptId,那么我必须使用 foreach 循环遍历每个键中的集合并形成嵌套列表。不使用 foreach 有没有更好的方法? 不,没有比你正在做的事情——分组和形成列表——将你的重复 A 和 B 的平面响应转换回树形图的“更好”方法了。任何替代技术最终都会回到同一件事 确实,我的回答总结如下:“您可能对 EF 可以为您做什么有一些不切实际的期望;您可能需要改变您的工作方式,以便使用 EF因为它被设计为使用,或使用其他东西并继续手动重建您的数据“.. 因此,如果我消除 EF,使用 Dapper.. 我在多个映射中看到的是我们必须将模型类传递给 Query 并且还必须指定 splitOn。现在,问题是,如果我的 SP 是由许多表之间的连接组成的,那么我必须传递所有模型类,对吗?是好方法吗?对于拆分,如果我的 SP 没有以正确的方式定义列选择怎么办,可能是随机方式,如 a.id、a.name、b.id、b.xyz、a.ghhjj、b .rtyjs ??它会起作用吗?.. 如果所有这些都不好,那么建议我调用 SP 从 .net 核心 api 获得许多连接的正确实用方法? 呃..您不必传递模型类,但是使用 EF 脚手架构建模型,然后使用 dapper 进行查询有点.. 奇怪。当然,它会起作用 - 归根结底只是 POCO。至于您的 SP,如果它以像 SELECT a.id, a.name, b.id, b.name, c.id, c.name 这样的良好顺序选择列,因此成为 spliton 的良好候选者,那就太好了。如果您选择像SELECT a.id, b.id, c.id, a.name, b.name, c.name 这样的选择,那是您以自己的方式抛出的另一个障碍,但您可以通过 dapper 中的一些手动列映射技术来绕过它。我会放弃 SP,但是 YMMV

以上是关于EF Core 调用具有多个连接的存储过程并映射相关数据的主要内容,如果未能解决你的问题,请参考以下文章

Entity Framework 6 中具有多个记录集的存储过程

如何通过 FromSqlRaw 在 EF Core 3.0 中调用存储过程

EF 调用存储过程

EF Core 存储过程错误:'@p1' 附近的语法不正确 [关闭]

asp.net core, Ef core : 在运行时动态映射存储库和服务

如何使用同一个连接表创建多个多对多关系 [EF7/Core]