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<string>(renkler.OrderBy(o => Guid.NewGuid()), 2);
替换renkler.OrderBy(o => Guid.NewGuid()).Take(2);
以确保。
实现DefaultColours()
也很好,因为它是一个 IQueryable。 -- @Neil:它是Queryable.Take()
-扩展,而不是Enumerable.Take()
。
【参考方案1】:
这可能对您没有解决方案,但很难用多行代码和图像格式化 cmets。
我很确定这是您的数据提供商的问题。也许这个组件没有按照它应该的方式实现Take()
。
我试图重建您的星座,但我没有使用任何 IQueryable
提供程序,而是构建了一个包含 500 个对象的 List<>
并在其上调用 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的主要内容,如果未能解决你的问题,请参考以下文章