【问题标题】:Nested query in entity framework实体框架中的嵌套查询
【发布时间】:2013-03-01 22:34:44
【问题描述】:

我收到以下异常:

不支持嵌套查询。 Operation1='案例' Operation2='收集'

用这个查询

var Games = context.Games.Select(a => new GameModel
{
     Members = (a.Type == 1 ? (a.UsersInGames.Where(b => b.GameID == a.ID && b.StatusID == 1).Select(c => new Member
     {
         ID = c.UserID,
         email = c.UserInfo.EmailAddress,
         screenName = c.UserInfo.ScreenName
     })) :   
    (a.Teams.Where(b => b.GameID == a.ID).SelectMany(b => b.UsersInTeams.Where(c => c.StatusID == 1)).Select(d => new Member
    {
        ID = d.UserID,
        email = d.UserInfo.EmailAddress,
        screenName = d.UserInfo.ScreenName
    )))
})

当我在选择成员时不包括条件时,查询工作正常。有没有办法可以在查询中执行条件?

【问题讨论】:

  • 在初始化时是否必须进行查询?
  • @NathanWhite 不,不是必须的。不过,我很想知道如何在查询中使用条件。

标签: c# linq entity-framework


【解决方案1】:

您高估了 LINQ 转换为 SQL 的能力。并非所有内容都是可翻译的,并且由于 LINQ 的工作方式,因此没有编译器警告。

嵌套集合通常要么 a) 不受支持,要么 b) 以可怕的 SELECT N+1 查询告终。您要求 EF 做的是返回一个对象树。 SQL 不支持树状结果,因此您会遇到对象-关系阻抗不匹配的问题,这很痛苦。

我建议您将嵌套集合数据作为第二个完全独立的查询来获取。这使您可以更好地控制并保证工作。

作为非必要的旁注,您可能无法说服 EF 使用 ?: 运算符而不是序列。这很难翻译。想一想您将如何将其编写为 SQL - 非常困难且令人费解。

【讨论】:

  • 我认为你关于三元 ?: 运算符的最后一段比你想象的更重要——这正是我得到与提问者相同的例外的原因。顺便说一句,谢谢你的回答。
  • 这些是从Linq2Sql 迁移而来的常见“问题”(这使您可以进行 EF 无法进行的各种疯狂翻译),但是我猜您的迁移部分是为了提高性能原因让你不得不告别 Linq2Sql 会生成的一些糟糕的 SQL。
  • @Simon_Weaver 我仍然在大型代码库上坚持使用 L2S,因为在 8 年的时间优势之后,EF 无法翻译 DateTime.Date 之类的东西。 L2S 是一个非常好的系统,我认为他们选择了错误的弃用系统。 L2S 也有提供者模型。他们只是将成员标记为私有(可能是出于时间和预算的原因。计划用于 v2)。到目前为止,我几乎没有抱怨不适用于 EF 的 L2S SQL 生成。虽然 EF 现在生成了相当不错的 SQL。我很想听听你的想法。
  • @usr L2S 只是允许我在每次需要时添加更多东西,并且随着时间的推移它变得越来越低效。当我们一次有 100 个订单(要打印)时这很好,但现在有些早上可能是 1000-2000 个,所以它刚刚失控。多年来我已经优化了 L2S,但我只需要重新开始并清理东西。变成了一个重构的兔子洞,但感谢 DateTime 的提醒。那个很快就会打到我的:-(
  • 我觉得<cond> ? <collect> : <collect> 可以天真地实现为UNION,其中第一个子查询执行WHERE <cond>,第二个执行WHERE NOT <cond>。它可能不是世界上最快的事情,但可能仍然比 N+1 查询更好,或者根本没有。无论如何,这就是为什么我仍然更喜欢为任何昂贵的东西写一个该死的 sproc。我发现解决方案通常不那么复杂,更灵活,运行速度也更快。对象被高估了。
【解决方案2】:

Linq to EF 似乎不支持以下内容

context.Games.Select(g => new
{
    Field = g.IsX? queryable1 : queryable2
});

但是,您可以使用以下 hack 来使其工作:

context.Games.Select(g => new
{
    Field = queryable1.Where(q => g.IsX)
               .Concat(queryable2.Where(q => !g.IsX))
});

【讨论】:

  • 我不会称之为“黑客”。取决于一个人追求什么,这可能是“说事情”的最佳方式:-)
  • 我需要在 EF 中按导航属性列表中的特定对象对结果集进行排序。它想要获取当前月份,但如果没有找到结果,我们想要返回第一个月。它天真地类似于... expr = x => (x.subset.Where(y => currentmonth).Count() > 0 ? x.subset.Where(y => currentmonth) : x.subset.OrderBy( y => month)).FirstOrDefault().property 我使用此代码转换为以下代码,一切正常,并转换为 SQL。 x => (x.subset.Where(y => currentmonth).Concat(x.subset.OrderBy(y => month))).FirstOrDefault().property;
【解决方案3】:

我遇到了同样的问题。解决方案是加载两个结果并确定在查询后使用什么(我知道它有性能下降),但如果截止日期攻击你,至少你可以暂时这样做:

在 LINQ 方面

  var Games = context.Games.Select(a => new GameModel
        {
            // carries type1 results
            Members = a.UsersInGames.Where(b => b.GameID == a.ID && b.StatusID == 1).Select(c => new Member
            {
                ID = c.UserID,
                email = c.UserInfo.EmailAddress,
                screenName = c.UserInfo.ScreenName
            })),

             //You need to create this temporary carrier to carry type 2 results
             MembersOfType2 = a.Teams.Where(b => b.GameID == a.ID).SelectMany(b => b.UsersInTeams.Where(c => c.StatusID == 1)).Select(d => new Member
                {
                    ID = d.UserID,
                    email = d.UserInfo.EmailAddress,
                    screenName = d.UserInfo.ScreenName
                })))
            })
        }

之后,您可以循环 Games 并为某个游戏分配 Members = MembersOfType2 if Type == 1

【讨论】:

    【解决方案4】:

    我也有这个错误。我有这样的代码:

    var Games = context.Games.Select(a => new GameModel
    {
        Members = (!filters.GetDatailedDataToo ? null : new List<MemberModel>())
    };
    

    null 用于? : 操作时会出现此错误。

    不是那种情况,写在这里,但是我浪费了很多时间,我想任何人都使用这种情况,谁搜索这个错误文本..

    【讨论】:

      猜你喜欢
      • 2014-05-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-26
      • 1970-01-01
      • 2020-09-24
      相关资源
      最近更新 更多