2012-01-06 3 views
0

У меня следующий запрос:Возврат случайный объект из БД (LINQ запрос)

Banner banner = Database.Banners.Where(b => b.IsPublish.Value && 
     b.Category.Value == (int) CategoryBanner.Banner && 
     b.PeriodShowCountAlready < b.PeriodShowCount || 
     b.ShowNext < DateTime.Now).Take(1).FirstOrDefault(); 

я получить только одну запись. Предположим, что в DB 3 записи удовлетворяют этому запросу, и LINQ будет возвращать всегда одну и ту же запись. Я хочу вернуть случайную запись, как я могу это сделать?

+2

http://stackoverflow.com/questions/648196/random-row-from-linq-to-sql –

+1

В '.Снять (1) .FirstOrDefault()' '.Снять (1)' часть полностью избыточна. – spender

ответ

3

В зависимости от базы данных, это может работать:

var banners = Database.Banners.Where(b => b.IsPublish.Value && 
     b.Category.Value == (int) CategoryBanner.Banner && 
     b.PeriodShowCountAlready < b.PeriodShowCount || 
     b.ShowNext < DateTime.Now); 

Тогда просто пропустить случайное количество баннеров ...

var skip = new Random().Next(banners.Count() - 1); 
var banner = banners.Skip(skip).FirstOrDefault(); 

Как указывает Амар, этот подход является менее оптимальным, поскольку он вызывает две поездки в банк данных. Из моего собственного опыта профилирования, поездка туда и обратно на другой сервер чаще всего стоит намного больше, чем выполнение простого оператора SQL.

Итак, какие у вас варианты?

  • Извлеките весь набор и выберите случайный баннер локально. Это не оптимально, потому что набор может быть очень большим. Если вы знаете, что набор будет небольшим (менее 1000 записей без двоичных полей [например, изображение баннера] или менее 10 тыс. Вообще должны быть незначительными)

  • Задайте в базе данных, сколько объектов есть в первую очередь, и затем спросите базу данных для n: th объекта. Это плохо, потому что это вызывает две поездки.

  • Сделать базу данных сделать все, что внутренне выходит из комфорта вашего ORM и записывает хранимую процедуру вручную. С этой целью вы используете newid() [на сервере sql] в качестве порядка по параметру, и все быстро и полезно, но вы должны делать это на сервере, а не в своем приложении C#.

+0

Это интересно. Неплохо :) –

2

Update: The (намного лучше) answer в дублированной вопрос дает пример с Skip. Преимуществом является пустая обработка списка. Кроме того, в этом ответе обсуждаются проблемы с большими наборами результатов, которые не будут хорошо обработаны в моем решении ...

Вы можете вернуть более одного из db и выбрать случайную запись из результата.

List<Banner> list = Database.Banners.Where(b => b.IsPublish.Value && 
     b.Category.Value == (int) CategoryBanner.Banner && 
     b.PeriodShowCountAlready < b.PeriodShowCount || 
     b.ShowNext < DateTime.Now).Take(10).ToList(); 

Random r = new Random(); 
Banner banner = list.Count == 0 ? null : list[r.Next(0, list.Count)]; 
+2

Но если предположить, что вместо того, чтобы брать случайную строку из первых 10 результатов, OP хочет один из набора из 1 миллиона результатов? Это тянет довольно много ненужных данных. – spender

0

Получить все записи и сделать случайным образом на них

Попробуйте сделать это:

var records = Database.Banners.Where(b => b.IsPublish.Value && 
b.Category.Value == (int) CategoryBanner.Banner && 
b.PeriodShowCountAlready < b.PeriodShowCount || 
b.ShowNext < DateTime.Now).ToList(); 
var random = new Random(); 
var count = random.Next(records.Count - 1); 
Banner banner = records[count]; 
+0

«диапазон возвращаемых значений обычно включает ноль, но не maxValue», поэтому использование 'random.Next (records.Count - 1)' никогда не создаст индекс для последнего элемента. Кроме того, поддерживаются ли операторы индекса IQueryable? Я так не думаю. – spender

+0

В этом примере нет ничего плохого, кроме 1, и того факта, что вы не хотите получать все баннеры, если вам нужен только один[email protected] - обратите внимание, что для записей были ToList() - ed. – Gleno

+0

Забыл сделать ToList на результат. IQueryable поддерживает Count() хотя –

Смежные вопросы