7

I try to get random record from database:

 personToCall = db.Persons.Skip(toSkip).Take(1).First();

but I get exception which tells me:

{"The method 'Skip' is only supported for sorted input in LINQ to Entities. The method 'OrderBy' must be called before the method 'Skip'."}

Can I do it without OrderBy? Sorting data structure (O(nlogn)) to pick random element(which should be constant) doesn't look wise.

EDIT: I use Entity Framework 6.1.1.

Yoda
  • 17,363
  • 67
  • 204
  • 344
  • Do you use `Identity` integer primary keys? – Lukazoid Sep 14 '14 at 16:06
  • Both the answers below should work for you . – DarthVader Sep 14 '14 at 16:07
  • @Lukazoid Didn't hear about them. The model looks like `public class Person { public int Id { get; set; } etc....}` and Entity Framework created table basing on this code. – Yoda Sep 14 '14 at 16:09
  • Even if you do I don't see how that would help. If they were guaranteed to be sequential and without gaps you could find the max and min with an efficient index operation then select a random number in that range with a seek. But they aren't guaranteed to be. – Martin Smith Sep 14 '14 at 16:12

4 Answers4

32

You can have something like :

personToCall = db.Persons.OrderBy(r => Guid.NewGuid()).Skip(toSkip).Take(1).First();

You should use FirstOrDefault to be mode defensive.

Here the dark lord teaching the force to yoda! what is the world coming to!

DarthVader
  • 52,984
  • 76
  • 209
  • 300
6

First you need to get the random number from 1 to max record, see this

Random rand = new Random();
int toSkip = rand.Next(0, db.Persons.Count());

db.Persons.Skip(toSkip).Take(1).First();

with order by you can use the Guid.NewGuid()

db.Persons.OrderBy(x=>x.Guid.NewGuid()).Skip(toSkip).Take(1).FirstOrDefault();
Timwi
  • 65,159
  • 33
  • 165
  • 230
Ali Adravi
  • 21,707
  • 9
  • 87
  • 85
5

There is no way to do this without an ordering clause.

personToCall = db.Persons.OrderBy(r => Random.Next()).First();

That could be slow depending on the size of your Persons table, so if you wanted to make this fast, you'd have to add a column to Person, or join it to a dictionary of random numbers and Person keys, then order by that. But that is rarely a wise solution.

Better to ask a higher level question about the overall task at hand.

Dave Markle
  • 95,573
  • 20
  • 147
  • 170
-1

To avoid the OrderBy, dump to a List and random pick against the Index:

VB

With New List(Of Persons)
    .AddRange(db.Persons)
    PersonToCall = .Item(New Random().Next(0, .Count - 1))
End With

C#

var people = new List<Persons>();
people.AddRange(db.Persons);
personToCall  = people.Item(new Random().Next(0, people.Count - 1));
MrGadget
  • 1,258
  • 1
  • 10
  • 19
  • 1
    If fetching the whole table doesn't scale for your purposes, another thought might be to have a View in your model on the database side: SELECT TOP 1 * FROM table ORDER BY NEWID() – MrGadget Jan 09 '16 at 05:14