IQueryable<T>和List<T>返回的结果不同。

11

如果我在Entity Framework的查询结果上使用Select,那么结果将会有4个项目。

如果我在IQueryable.ToList()上使用Select,则将获得全部36个项目。

以下是该函数的代码:

public ImagesGetModelView Get(int start, int count)
{
    if (count <= 0) count = 9;
    else if (count > ImageHandler.MaxResult) count = ImageHandler.MaxResult;    

        IQueryable<Image> imagesList = ImagesHandler.FetchRangeScore(start, count)
           .Where(m => m.Domain == Database.Enums.ImageDomain.Gfycat);

        //Works using list :(
        //var list = imagesList.ToList();

        //Select all subreddits once
        //Returns 4 instead of 36 if not using the list ...
        //Returns 1 instead of 2 with Distinct() if not using the list
        IEnumerable<Subreddit> subreddits = imagesList
           .Select(m => m.Subreddit); //.Distinct();           

        ImagesGetModelView result = new ImagesGetModelView()
        {
            Items = imagesList,
            Subreddits = subreddits
        };

        return result;
    } 

public IQueryable<Image> FetchRangeScore(int a_start, int a_count)
    {
        return Repository.AllQueryable().OrderByDescending(m => m.Score)
          .Skip(a_start).Take(a_count);
    }

在这36个项目中,有2个子版面是不同的。但由于只有从Select()中获取了其中的4个,因此只找到了1个不同的。

那么是否可以通过LINQ表达式来获取正确的数据,使Distinct语句起作用,还是必须在继续使用Select和Distinct函数之前将其转换为List?

编辑:
通过将where语句从整个查询的末尾移动到开头。 现在似乎可以正常工作了。Select返回所有36个项目等...这反过来使Distinct工作,因为它可以找到不止一个唯一值。

 public IQueryable<Image> FetchRangeScore(int a_start, int a_count)
    {
        return Repository.AllQueryable()
          .Where(m => m.Domain == Database.Enums.ImageDomain.Gfycat)
          .OrderByDescending(m => m.Score)
          .Skip(a_start).Take(a_count);
    }

一些模拟数据可能有助于阐明你的观点。 - neontapir
1
我建议将您的逻辑分成不同版本(或者是您所更改的第二个代码片段)。对于需要变化的代码,跟随注释可能会很困难(例如,我无法确定您的示例是出现了问题还是正常工作)。 - Guvante
1
你在调用distinct之前分页有什么原因吗?我通常会期望相反的顺序。 - Guvante
2个回答

7

很可能你的 SQL Server 中的 Where 子句与 .NET 中的表现不同。具体来说,根据你的排序设置等,各种 .Domain 值可能仅因大小写不同等原因而在 SQL 中“相等”,但在 C# 中却不然。

你可以在 IQueryable<> 上捕获 .ToString() ,查看生成的 SQL 并自行尝试。

IQueryable<Image> imagesList = ImagesHandler.FetchRangeScore(start, count)
   .Where(m => m.Domain == Database.Enums.ImageDomain.Gfycat);
Debug.WriteLine(imagesList.ToString());

我相信你是正确的。你让我意识到我在那里加了一个where语句 :) 我很久以前添加了它进行测试,然后就变得盲目了。当你看到它时,它看起来非常酷,但数据集是36个子版面中的1种,所以我直到昨晚才注意到它是错误的。当我将它移到查询的开头时,我注意到Debug.WriteLine()不再返回子查询,一切似乎也都正常工作了。 - Luna

5
没有源数据很难确定,但是当Distinct被推送到SQL时,它的工作方式会有所不同。
SQL中的DISTINCT查询将提取所有值都不同的记录。当您在内存中对对象列表执行Distinct调用时,默认情况下会使用实例相等性。因此,您可能会得到“重复”的对象(所有字段值都相同的对象),但它们将是不同的“实例”,因此Distinct将把它们视为不同的对象。
因此,这取决于您想要什么 - 您想要包括重复项的所有Subreddit值还是想要不同的
回答您的问题 - 如果您不希望将Distinct调用传递到SQL,请调用AsEnumerable()而不是ToList,这使所有后续的Linq查询成为Linq-to-Objects(IEnumerable<T>)查询,而不是Linq-to-{whatever}(IQueryable<T>)查询,而无需将项目放入列表中。

我只需要不重复的值。我需要将子版块在结果中明确分开。 - Luna

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接