【问题标题】:EntityFrameworkCore PostgreSQL Left Join with where not workingEntityFrameworkCore PostgreSQL Left Join with where not working
【发布时间】:2019-12-27 13:15:55
【问题描述】:

我正在尝试使用 Entity Framework Core 执行左连接,但它在连接中创建了一个不起作用的子查询。

我创建了一个sample project 来说明问题

这是我的 Linq 查询,它从一个帐户中选择所有流以及该流期间的订阅者

Linq 查询语法

    from account in context.Accounts
    where account.Id == 6
    join steam in context.Streams on account.Id equals steam.AccountId
    join subscription in context.Subscriptions on account.Id equals subscription.TargetAccountId into
        groupJoin
    from subscription in groupJoin.Where(subscription => subscription.Date > steam.StreamStart && subscription.Date < steam.StreamEnd).DefaultIfEmpty()
    select new {account, steam, subscription};

Linq 扩展方法语法

    context.Accounts.Where(account => account.Id == 6)
        .Join(context.Streams, outer => outer.Id, inner => inner.AccountId,
        (account, stream) => new { Account = account, Stream = stream })
        .GroupJoin(context.Subscriptions, outer => outer.Account.Id, inner => inner.TargetAccountId,
        (outer, subscriptions) => new
        { Account = outer.Account, Stream = outer.Stream, Subscriptions = subscriptions })
        .SelectMany(x =>
            x.Subscriptions.Where(subscription =>
                    subscription.Date > x.Stream.StreamStart && subscription.Date < x.Stream.StreamEnd)
                .DefaultIfEmpty(),
        (x, subscription) => new { x.Account, x.Stream, Subscription = subscription }).ToList();

这是生成的查询

    SELECT account."Id", account."Name", stream."Id", stream."AccountId", stream."StreamEnd", stream."StreamStart", stream."Title", t."SourceAccountId", t."TargetAccountId", t."Date"
          FROM "Accounts" AS account
          INNER JOIN "Streams" AS stream ON account."Id" = stream."AccountId"
          LEFT JOIN (
              SELECT "inner"."SourceAccountId", "inner"."TargetAccountId", "inner"."Date"
              FROM "Subscriptions" AS "inner"
              WHERE ("inner"."Date" > stream."StreamStart") AND ("inner"."Date" < stream."StreamEnd")
          ) AS t ON account."Id" = t."TargetAccountId"
          WHERE account."Id" = 6

这给了我以下错误

Npgsql.PostgresException (0x80004005): 42P01: 无效引用 表“流”的 FROM 子句条目

我的 Linq 查询是错误的还是实体框架中的错误/限制?

【问题讨论】:

  • 错误/限制。您使用的是什么 EF Core(查看它是否已在更高版本中修复或如何解决它)?
  • 我正在使用 Npgsql.EntityFrameworkCore.PostgreSQL 2.2.4
  • 注意:尽量避免保留/关键字(日期,内部)即使Postgres允许它们,您的框架也可能被它们触发。

标签: c# postgresql linq entity-framework-core


【解决方案1】:

绝对是 EF Core 查询翻译错误 - 使用最新的 EF Core 2.2.6 和 SqlServer 提供程序重现,因此它不是特定于 Npgsql 提供程序的。

由于 3.0 将使用完全重写的查询翻译器,并且在当前预览中很多东西都不能正常工作,所以不能说它是否会在那里修复。

解决方法是将相关过滤器移到join 之外(加上额外的null 检查以说明DefaultIfEmpty() 引入的left outer join):

var result = context.Accounts
    .Where(account => account.Id == 6)
    .Join(context.Streams, outer => outer.Id, inner => inner.AccountId,
        (account, stream) => new { Account = account, Stream = stream })
    .GroupJoin(context.Subscriptions, outer => outer.Account.Id, inner => inner.TargetAccountId,
        (outer, subscriptions) => new
        { Account = outer.Account, Stream = outer.Stream, Subscriptions = subscriptions })
    .SelectMany(x => x.Subscriptions.DefaultIfEmpty(),
        (x, subscription) => new { x.Account, x.Stream, Subscription = subscription })
    .Where(x => x.Subscription == null || (
        x.Subscription.Date > x.Stream.StreamStart &&
        x.Subscription.Date < x.Stream.StreamEnd))
    .ToList();

【讨论】:

  • 但这不包括没有订阅的流,因为在组加入中添加了每个订阅(因为没有日期检查)并且 where 将它们过滤掉。所以 x.Subscription == null 已经过时了
  • 不,不是(至少这是您的查询在工作时会执行的操作)。由于Where 条件之后的DefaultIfEmpty(),原始查询确实包含它们。基本上这确实是左外连接。如果不需要,请删除 DefaultIfEmpty(这会将连接变为内部)并进行空检查)。
  • 但是如果所有订阅都在日期范围之外,可能会有所不同。我不知道如何解决这种情况。
  • .Where(x =&gt; x.Subscription == null 仅在该帐户有 0 次订阅时才有用。因为join中没有time check,所以每次订阅都会在join中结束。
  • 这是可能的情况,对吧?个人比较关心的是上一条评论中提到的另外一种情况,这里不做处理。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-04-28
  • 2017-03-20
  • 1970-01-01
  • 2021-02-15
  • 2017-07-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多