1

在我正在开发的 Web 应用程序中,我在 Windows 环境中使用 .NET Core 3.1 和 SQL Server。

cshtml页面的支持代码中,我创建了一个 LINQ 查询以从数据库的多个表中获取信息。
这是代码片段,就我使用 SQL Server 而言,它运行良好。

var cupEditions = await _context.CupEditions
   .Where(c => c.CupID == nationalCupId)
   .OrderBy(c => c.Season)
   .Select(c => new {
      c.ID,
      c.Season,
      Final = c.CupDays.Single(cd => cd.Turn == 0).Matches.First()
   })
   .ToListAsync();

为了从开发人员转移到生产环境,作为一个爱好应用程序,我决定将数据库移动到更便宜的 MySQL 数据库,从 SQL Server EF Core 移动(有些头疼)到 MySQL EF Core。

上面的指令,在转换到 MySQL EF Core 包之后(我不知道,但我可以验证是否需要,如果它与 SQL Server EF Core 包相同),生成以下查询:

FROM `CupEditions` AS `c`
      OUTER APPLY (
          SELECT `c0`.`ID`, `c0`.`AwayTeamID`, `c0`.`CupDayID`, `c0`.`Data`, `c0`.`HomeTeamID`, `c0`.`gridID`
          FROM `CupMatches` AS `c0`
          WHERE (
              SELECT `c1`.`ID`
              FROM `CupDays` AS `c1`
              WHERE (`c`.`ID` = `c1`.`CupEditionID`) AND (`c1`.`Turn` = 0)
              LIMIT 1) IS NOT NULL AND (((
              SELECT `c2`.`ID`
              FROM `CupDays` AS `c2`
              WHERE (`c`.`ID` = `c2`.`CupEditionID`) AND (`c2`.`Turn` = 0)
              LIMIT 1) = `c0`.`CupDayID`) OR ((
              SELECT `c2`.`ID`
              FROM `CupDays` AS `c2`
              WHERE (`c`.`ID` = `c2`.`CupEditionID`) AND (`c2`.`Turn` = 0)
              LIMIT 1) IS NULL AND `c0`.`CupDayID` IS NULL))
          LIMIT 1
      ) AS `t`
      WHERE `c`.`CupID` = @__nationalCupId_0
      ORDER BY `c`.`Season`

我无法判断是否有更好、更有效的方法来进行此查询。无论如何,MySQL 似乎不支持该OUTER APPLY指令,因此查询失败生成异常。我发现解决此问题的方法是向数据库发出多个请求,而不是单个 LINQ 查询,这种解决方案似乎很不优雅。

有谁知道如何更好地设计 LINQ 代码以提高效率和可读性,使其与 MySQL 一起使用?

4

1 回答 1

1

首先,有两个 MySQL数据库提供程序,看起来您正在使用MySql.EntityFrameworkCore。在许多情况下,另一个Pomelo.EntityFrameworkCore.MySql工作得更好,只需切换到它就可以解决问题(不幸的是,LINQ 的想法因引入而死IQueryable<T>- 查询编译,但不在运行时执行)。

但是,为了避免(在大多数情况下)这样的问题,有一些编写 LINQ 查询的规则。一种是避免结构,如First,等FirstOrDefaultSingle然后是成员访问(如.Matches在您的查询中)。相反,首先使用适当的Select/SelectMany方法,最后保留行限制运算符。

将其应用于您的查询将是这样的

Final = c.CupDays
    .Where(cd => cd.Turn == 0)
    .SelectMany(cd => cd.Matches)
    .Single()

但是两个提供者(通常是所有提供者)都应该能够翻译它,因为它在幕后是基于简单的左外连接。

于 2021-10-23T10:20:33.547 回答