在Npgsql中为相同的导航属性进行重复连接

本文关键字:属性 连接 导航 Npgsql | 更新日期: 2023-09-27 18:18:47

这个问题已经存在了,但是对于实体框架的TSQL

当在select中多次使用相同的导航属性时,Npgsql查询会产生多个连接,每次使用导航属性都会产生一个连接。这将导致一个糟糕的性能打击(测试)

我读到这是EF 4的问题,但这个问题也发生在EF 6。

我认为这是Npgsql LINQ到SQL转换器的问题

这是Npgsql为多次使用的相同导航属性生成的代码,显然,只需要一个连接(从其他问题复制,因为是完全相同的情况)

LEFT OUTER JOIN [dbo].[Versions] AS [Extent4] ON [Extent1].[IDVersionReported] = [Extent4].[ID]
LEFT OUTER JOIN [dbo].[Versions] AS [Extent5] ON [Extent1].[IDVersionReported] = [Extent5].[ID]
LEFT OUTER JOIN [dbo].[Versions] AS [Extent6] ON [Extent1].[IDVersionReported] = [Extent6].[ID]
LEFT OUTER JOIN [dbo].[Versions] AS [Extent7] ON [Extent1].[IDVersionReported] = [Extent7].[ID]

是否可以调优PostgreSql来优化重复连接?

如果不是,哪个选项是解决这个问题的最佳选择?

  • 等待Npgsql修复
  • 下载Npgsql代码并找到修复方法
  • 在到达数据库之前拦截生成的SQL,解析它,并删除重复的连接。(点击此处阅读)
  • 不使用导航属性,使用LINQ连接代替

在Npgsql中为相同的导航属性进行重复连接

确实,这是一个实体框架的问题,但我找到了一个解决方案,希望这有助于有人。

这是LINQ查询的原始where部分:

 from cr in Creditos
 where cr.validado == 1 &&
 cr.fecharegistro >= Desde &&
 cr.fecharegistro <= Hasta &&
 !ProductosExcluidos.Contains(cr.idproducto.Value) &&
 cr.amortizaciones.Sum(am => am.importecapital - am.pagoscap - am.capcancel) > 1
 //All references to the navigation property cr.numcliente
 //results on a separated LEFT OUTTER JOIN between this the 'creditos' and 'clientes' tables
 select new ArchivoCliente
 {
    RFC = cr.numcliente.rfc,
    Nombres = cr.numcliente.nombres,
    ApellidoPaterno = cr.numcliente.apellidopaterno,
    ApellidoMaterno = cr.numcliente.apellidomaterno,
 }

注意where的最后一个条件,该条件对cr的所有子实体求和,如果我们取出最后一个条件,所有重复的LEFT OUTTER JOIN都被一个JOIN取代,由于某种原因,实体框架不喜欢查询where部分的子查询或聚合

如果我们将原来的查询替换为另一个等价的查询,则只生成一个LEFT OUTTER JOIN

 (from cr in Creditos
 where cr.validado == 1 &&
 cr.fecharegistro >= Desde &&
 cr.fecharegistro <= Hasta &&
 !ProductosExcluidos.Contains(cr.idproducto.Value) &&
 //Excluded aggregate function condition from the first where
 //the value is now on the select and used for posterior filtering
 select new ArchivoCliente
 {
    RFC = cr.numcliente.rfc,
    Nombres = cr.numcliente.nombres,
    ApellidoPaterno = cr.numcliente.apellidopaterno,
    ApellidoMaterno = cr.numcliente.apellidomaterno,
    SumaAmort = cr.amortizaciones.Sum(am => am.importecapital - am.pagoscap - am.capcancel)
 }).Where (x => x.SumaAmort > 1);

现在不是直接过滤第一个where语句,而是将聚合结果存储为投影的一部分,然后将第二个where应用于结果查询。

这将导致更快的查询,在翻译后的SQL语句上只有必要的连接。