Lambda 表达式 order by 和 take issue

Posted

技术标签:

【中文标题】Lambda 表达式 order by 和 take issue【英文标题】:Lambda expressions order by and take issue 【发布时间】:2016-04-12 23:58:46 【问题描述】:

我有一个带有COLOURS 类类型的IQueryable 列表

IQueryable<COLOURS> renkler = dbcontext.colours.Select(s=>new COLOURS .... 

我想随机获取 2 行,我正在使用此代码块来执行此操作:

renkler.OrderBy(o => Guid.NewGuid()).Take(2);

我想要 2 行,但有时需要 3 行或 5 行:

Take(2) 不工作 - 有什么问题?

我检查的时候发现了一些东西

var result = NewProducts().OrderBy(o => Guid.NewGuid()).Take(2);
int result_count = result.Count(); //This value is 2 :D
                                   //but ToList() result 5 :D

整个方法:

public IQueryable<COLOURS> NewProducts() 

    DateTime simdi = DateTime.Now;
    DateTime simdi_30 = DateTime.Now.AddDays(-30);

    var collection_products = DefaultColours()
                             .Where(w => ((w.add_date.Value >= simdi_30 && w.add_date.Value <= simdi) || w.is_new == true))
                             .OrderByDescending(o => o.add_date).Take(200)
                             .Select(s => new COLOURS
                             
                                 colour_code = s.colour_code,
                                 model_code = s.products.model_code,
                                 sell_price = (decimal)s.sell_price,
                                 market_price = (decimal)s.market_price,
                                 is_new = (bool)s.is_new,
                                 product_id = (int)s.product_id,
                                 colour_name = s.name,
                                 product_name = s.products.name,
                                 description = s.products.description,
                                 img_path = s.product_images.FirstOrDefault(f => f.is_main == true).img_path,
                                 category_id = (int)s.category_relations.FirstOrDefault().category_id,
                                 display_order = (short)s.display_order,
                                 section_id = (int)s.products.section_id,
                                 stock_amount = s.pr_sizes.Where(w => w.is_active == true && w.quantity >= 0).Count() > 0 ? (int)s.pr_sizes.Where(w => w.is_active == true && w.quantity >= 0).Sum(s2 => s2.quantity) : 0,
                                                                      section_name = s.products.pr_sections.name,

                             );    
    return collection_products;


public IQueryable<COLOURS> RandomNewProducts(int n) 

    var result = NewProducts().OrderBy(o => Guid.NewGuid()).Take(n);
    int result_count = result.Count(); //2
    //When I run this method it's getting 5 rows               
    return result;

【问题讨论】:

这可能会或可能不会影响最终查询。您还可以显示悬停result 时显示的查询吗?我希望像SELECT TOP 2 * FROM (SELECT TOP 200 * FROM ...) 这样的东西。 @theEmge:很多人都想在这里帮助你,他们都在说“给我看代码”。他们想要代码,以便他们可以重现您的症状,以便找出问题所在。请停止将部分代码 sn-ps 放入 cmets - 将整个代码放入问题中,否则您只是在浪费每个人的时间。如果您可以将代码精简为仍然存在问题的更简单的代码,那就更好了。 这看起来像 System.Linq.Enumerable 上的 Take() 扩展方法,但真的是这样吗?它可能是不同类上的另一个同名方法吗?请发布您的所有代码,包括引用/“使用”语句和 DefaultColours() 方法。 @Neil 这实际上可能会发生,我认为编译器会警告您歧义,但我只是测试了它,显然它没有。 OP 应该尝试用System.Linq.Enumerable.Take&lt;string&gt;(renkler.OrderBy(o =&gt; Guid.NewGuid()), 2); 替换renkler.OrderBy(o =&gt; Guid.NewGuid()).Take(2); 以确保。 实现DefaultColours() 也很好,因为它是一个 IQueryable。 -- @Neil:它是Queryable.Take()-扩展,而不是Enumerable.Take() 【参考方案1】:

这可能对您没有解决方案,但很难用多行代码和图像格式化 cmets。

我很确定这是您的数据提供商的问题。也许这个组件没有按照它应该的方式实现Take()

我试图重建您的星座,但我没有使用任何 IQueryable 提供程序,而是构建了一个包含 500 个对象的 List&lt;&gt; 并在其上调用 AsQueryable() 以满足方法签名。

    public static IQueryable<COLOURS> DefaultColours()
    
        const int COUNT = 500;

        List<COLOURS> x = new List<COLOURS>();

        var startDate = DateTime.Today.AddDays(-1 * (int)(COUNT / 2));

        // add 500 date values, and use the date and any random bool value
        for (int i = 0; i < COUNT; i++)
            x.Add(new COLOURS()  add_date = startDate.AddDays(i), is_new = i % 3 == 0 );

        return x.AsQueryable();
    

但是当我这样做时,Take() 方法每次总是返回两个(不同的)项目 - 正如任何人所期望的那样:

【讨论】:

【参考方案2】:

这可能是由于 o => Guid.NewGuid() lambda 造成的。

排序算法需要一个与每个元素相关联的唯一键。调用 Guid.NewGuid() 意味着任何给定的元素都可以有多个与之关联的键,具体取决于它何时被调用。 Linq 试图在它如何在集合上运行时投机取巧,因此这可能会导致例如在排序操作期间,最低的两个元素突然不再是最低的两个元素。

考虑尝试对随机整数列表进行排序,每次排序算法尝试检索这些整数时,这些整数都会随机变化。唯一可行的方法是,如果排序算法保证对每个元素调用一次键函数,并且只调用一次。

OrderBy 的文档并没有说明是否允许排序算法对每个元素多次调用 key 函数,所以最好假设最坏的情况,除非你能证明不是这样。

作为一种(希望如此)简单的测试方法,如果您可以临时将随机键包含为 COLOR 对象的持久元素,以便在排序过程中顺序不会改变,那么 .Take() 应该开始完全按照故意的。

此外,Guid.NewGuid() 并不快,因此将临时测试变成永久解决方案,为每个对象使用更多内存也可以提高代码的速度。

【讨论】:

以上是关于Lambda 表达式 order by 和 take issue的主要内容,如果未能解决你的问题,请参考以下文章

SQL的关于ORDER BY 表达式

order by 和 group by 的区别?

Group by、Count 和 Lambda 表达式

Lambda表达式 group by having问题

在数据库查询中order by 后面可以给表达式吗?

ACCESS 的 ORDER BY 问题