Лямбда-выражения упорядочиваются и берут вопрос

У меня есть список IQueryable с типом класса COLOURS

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 getting 5 rows               
    return result;
}

Ответ 1

Это может быть вам не решением, но сложно форматировать комментарии с помощью многострочного кода и изображений.

Я уверен, что это проблема с вашим поставщиком данных. Возможно, этот компонент не реализует Take() так, как он должен.

Я попытался перестроить ваше созвездие, но вместо какого-либо поставщика IQueryable я построил List<> с 500 объектами и назвал 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() означает, что любой данный элемент может иметь несколько ключей, связанных с ним, в зависимости от того, когда он вызван. Линк пытается быть оппортунистическим в том, как он работает на множестве, поэтому это может привести к тому, самые низкие два элемента внезапно перестают быть самыми низкими двумя элементами во время операции сортировки.

Рассмотрим попытку сортировки списка случайных чисел, где эти целые числа случайным образом меняются каждый раз, когда алгоритм сортировки пытается их восстановить. Единственный способ, которым это могло бы работать, - это алгоритм сортировки, обеспечивающий возможность вызова ключевой функции один раз и только один раз для каждого элемента.

В документации на OrderBy не указано, разрешено ли алгоритму сортировки вызывать функцию ключа более одного раза для каждого элемента, поэтому лучше всего принять наихудший случай, если вы не можете доказать обратное.

Как можно надеяться, простой способ проверить это, если вы можете временно включить случайный ключ в качестве постоянного элемента вашего объекта COLOR, чтобы порядок не изменялся во время сортировки,.Take() должен начинать работать точно так же, как и предназначена.

Кроме того, Guid.NewGuid() работает не быстро, поэтому включение этого временного теста в постоянное решение, которое использует немного больше памяти для каждого объекта, также может улучшить скорость вашего кода.