40

How do i return matching entities in a random order?
Just to be clear this is Entity Framework stuff and LINQ to Entities.

(air code)

IEnumerable<MyEntity> results = from en in context.MyEntity
                                where en.type == myTypeVar
                                orderby ?????
                                select en;

Thanks

Edit:
I tried adding this to the context:

public Guid Random()
{
    return new Guid();
}

And using this query:

IEnumerable<MyEntity> results = from en in context.MyEntity
                                where en.type == myTypeVar
                                orderby context.Random()
                                select en;

But i got this error:

System.NotSupportedException: LINQ to Entities does not recognize the method 'System.Guid Random()' method, and this method cannot be translated into a store expression..

Edit (Current code):

IEnumerable<MyEntity> results = (from en in context.MyEntity
                                 where en.type == myTypeVar
                                 orderby context.Random()
                                 select en).AsEnumerable();
NikolaiDante
  • 18,469
  • 14
  • 77
  • 117

12 Answers12

55

A simple way of doing this is to order by Guid.NewGuid() but then the ordering happens on the client side. You may be able to persuade EF to do something random on the server side, but that's not necessarily simple - and doing it using "order by random number" is apparently broken.

To make the ordering happen on the .NET side instead of in EF, you need AsEnumerable:

IEnumerable<MyEntity> results = context.MyEntity
                                       .Where(en => en.type == myTypeVar)
                                       .AsEnumerable()
                                       .OrderBy(en => context.Random());

It would be better to get the unordered version in a list and then shuffle that though.

Random rnd = ...; // Assume a suitable Random instance
List<MyEntity> results = context.MyEntity
                                .Where(en => en.type == myTypeVar)
                                .ToList();

results.Shuffle(rnd); // Assuming an extension method on List<T>

Shuffling is more efficient than sorting, aside from anything else. See my article on randomness for details about acquiring an appropriate Random instance though. There are lots of Fisher-Yates shuffle implementations available on Stack Overflow.

Community
  • 1
  • 1
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Hi Jon, I tried this, but couldn't get it working - see my edit. Thanks Nath – NikolaiDante Mar 17 '09 at 16:13
  • I don't see a .ToEnumerable() am I missing a namespace? – NikolaiDante Mar 17 '09 at 16:21
  • Hi Jon, I still get the same error as above with that change. Also the .OrderBy(context => context.Random()); line doesn't work as I get an error about redifining the context. I'll update my q with my latest query. Thanks – NikolaiDante Mar 17 '09 at 16:40
  • I'm so sorry - this is what comes of trying to answer in tiny bits of free time. The AsEnumerable() should be fine, but the lambda expression was broken. Try it now. – Jon Skeet Mar 17 '09 at 17:13
  • No worries, your help is appreciated. :o) I'm just commenting out my changes from Toro's answer and I'm giving it a go. – NikolaiDante Mar 17 '09 at 17:22
  • doesn't seem to give a random result tho. – NikolaiDante Mar 18 '09 at 11:12
  • 1
    Maybe it's new in EF4, but you can do this in the DB: http://stackoverflow.com/questions/654906/linq-to-entities-random-order/4120132#4120132 – Drew Noakes Nov 07 '10 at 22:49
  • @DeletedAccount, this technique does give random results for me at least: http://stackoverflow.com/questions/654906/linq-to-entities-random-order/4120132#4120132 – Drew Noakes Nov 07 '10 at 22:50
  • Guid.newGuid() would be a relatively heavy way of randomising as a Guid is held in memory as 128-bits, better would be a int at 32-bit or double at 64-bits, though Guid.newGuid() will probably provide a more realistically random mix – MikeT Jun 05 '14 at 15:19
  • [Professional ASP.NET MVC 5](http://www.amazon.com/Professional-ASP-NET-MVC-Jon-Galloway/dp/1118794753/ref=sr_1_1?ie=UTF8&qid=1409025449&sr=8-1&keywords=professional+asp.net+mvc+5) gives the credit to this Jon Skeet's answer in Page 228. – Win Aug 26 '14 at 03:58
  • Ordering by an unstable ranking function looks unsafe. Depending on the underlying sort algorithm, it may leads to bugs. See [this blog](https://lostechies.com/patricklioi/2013/09/14/shufflin-aint-sortin/) for details and [this question](http://stackoverflow.com/q/7952147/1178314) for a resulting bug case. – Frédéric Sep 22 '15 at 08:20
  • @Frederic: OrderBy in LINQ to Objects only evaluates the key projection once per element. It's certainly not what I'd use myself these days though - will edit. – Jon Skeet Sep 22 '15 at 08:22
  • Ok, thanks for this info. The bug case occurs in linq-to-entities, not linq-to-objects. But since current EF supports NewGuid, many dev remove the AsEnumerable to get it ordered DB side. Then in case of includes in their queries, they may get duplicated results with EF 6. – Frédéric Sep 22 '15 at 08:29
  • @Frederic: Right. I'll edit the first sentence to indicate that. – Jon Skeet Sep 22 '15 at 08:40
41

Jon's answer is helpful, but actually you can have the DB do the ordering using Guid and Linq to Entities (at least, you can in EF4):

from e in MyEntities
orderby Guid.NewGuid()
select e

This generates SQL resembling:

SELECT
[Project1].[Id] AS [Id], 
[Project1].[Column1] AS [Column1]
FROM ( SELECT 
    NEWID() AS [C1],                     -- Guid created here
    [Extent1].[Id] AS [Id], 
    [Extent1].[Column1] AS [Column1],
    FROM [dbo].[MyEntities] AS [Extent1]
)  AS [Project1]
ORDER BY [Project1].[C1] ASC             -- Used for sorting here

In my testing, using Take(10) on the resulting query (converts to TOP 10 in SQL), the query ran consistently between 0.42 and 0.46 sec against a table with 1,794,785 rows. No idea whether SQL Server does any kind of optimisation on this or whether it generated a GUID for every row in that table. Either way, that would be considerably faster than bringing all those rows into my process and trying to sort them there.

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
  • Note: If you run this code in LinqPad, and it doesn't work, this may help: http://stackoverflow.com/a/20953863/740639 – Walter Stabosz Jan 06 '14 at 16:04
  • 1
    See [this question](http://stackoverflow.com/q/7952147/1178314), it is unfortunately broken. It looks like `OrderBy` assumes the ranking function to be stable, which is not the case with a random generator. Linq to entities translate this to a sql query which may get different ranking for the same entity (as soon as your queries use `Include`). Then it causes the entity to get duplicated in the result list. – Frédéric Jul 06 '15 at 09:46
30

The simple solution would be creating an array (or a List<T>) and than randomize its indexes.

EDIT:

static IEnumerable<T> Randomize<T>(this IEnumerable<T> source) {
  var array = source.ToArray();
  // randomize indexes (several approaches are possible)
  return array;
}

EDIT: Personally, I find the answer of Jon Skeet is more elegant:

var results = from ... in ... where ... orderby Guid.NewGuid() select ...

And sure, you can take a random number generator instead of Guid.NewGuid().

Paul Fleming
  • 24,238
  • 8
  • 76
  • 113
Michael Damatov
  • 15,253
  • 10
  • 46
  • 71
  • 1
    Hi toro, Sorry, I'm not seeing what method to use on a List, could you elaborate? thanks. – NikolaiDante Mar 17 '09 at 16:15
  • 1
    There isn't a framework method to do this. I suggest http://en.wikipedia.org/wiki/Fisher-Yates_shuffle – mqp Mar 17 '09 at 16:44
  • Thanks mquander, that's what I was after. – NikolaiDante Mar 17 '09 at 17:14
  • 1
    I want to do this against millions of rows and so can't realistically bring all those entities into my context and start sorting them. You can do this in the database using a variation of Jon's answer I posted below: http://stackoverflow.com/questions/654906/linq-to-entities-random-order/4120132#4120132 – Drew Noakes Nov 07 '10 at 22:51
  • 1
    Ordering by an unstable ranking function looks unsafe. Depending on the underlying sort algorithm, it may leads to bugs. See [this blog](https://lostechies.com/patricklioi/2013/09/14/shufflin-aint-sortin/) for details and [this question](http://stackoverflow.com/q/7952147/1178314) for a resulting bug case. – Frédéric Sep 22 '15 at 08:20
  • I wouldn't do it either because shuffling is an O(n) algorithm, and sorting is an O(n log n) one. – Michael Damatov Sep 23 '15 at 21:14
4

The NewGuid hack for sorting it server side unfortunately causes entities to get duplicated in case of joins (or eager fetching includes).

See this question about this issue.

To overcome this issue, you may use instead of NewGuid a sql checksum on some unique value computed server side, with a random seed computed once client side to randomize it. See my answer on previously linked question.

Community
  • 1
  • 1
Frédéric
  • 9,364
  • 3
  • 62
  • 112
2

The solutions provided here execute on the client. If you want something that executes on the server, here is a solution for LINQ to SQL that you can convert to Entity Framework.

Fabrice
  • 3,094
  • 3
  • 28
  • 31
  • 2
    This solution may cause execution on the server, but it also requires that you have access to setup Functions and Views on the SQL box itself. – Jared Apr 17 '13 at 11:58
1

lolo_house has a really neat, simple and generic solution. You just need to put the code in a separate static class to make it work.

using System;
using System.Collections.Generic;
using System.Linq;

namespace SpanishDrills.Utilities
{
    public static class LinqHelper
    {
        public static IEnumerable<T> Randomize<T>(this IEnumerable<T> pCol)
        {
            List<T> lResultado = new List<T>();
            List<T> lLista = pCol.ToList();
            Random lRandom = new Random();
            int lintPos = 0;

            while (lLista.Count > 0)
            {
                lintPos = lRandom.Next(lLista.Count);
                lResultado.Add(lLista[lintPos]);
                lLista.RemoveAt(lintPos);
            }

            return lResultado;
        }
    }
}

Then to use the code just do:

var randomizeQuery = Query.Randomize();

So simple! Thank you lolo_house.

Mythlandia
  • 576
  • 1
  • 5
  • 15
0

I think it's better not to add properties to the class. Better to use the position:

public static IEnumerable<T> Randomize<T>(this IEnumerable<T> pCol)
    {
        List<T> lResultado = new List<T>();
        List<T> lLista = pCol.ToList();
        Random lRandom = new Random();
        int lintPos = 0;

        while (lLista.Count > 0)
        {
            lintPos = lRandom.Next(lLista.Count);
            lResultado.Add(lLista[lintPos]);
            lLista.RemoveAt(lintPos);
        }

        return lResultado;
    }

And the call will (as toList() or toArray()):

var result = IEnumerable.Where(..).Randomize();

lolo_house
  • 69
  • 1
  • 4
0

(cross-posting from EF Code First: How to get random rows)

Comparing two options:


Skip(random number of rows)

Method

private T getRandomEntity<T>(IGenericRepository<T> repo) where T : EntityWithPk<Guid> {
    var skip = (int)(rand.NextDouble() * repo.Items.Count());
    return repo.Items.OrderBy(o => o.ID).Skip(skip).Take(1).First();
}
  • Takes 2 queries

Generated SQL

SELECT [GroupBy1].[A1] AS [C1]
FROM   (SELECT COUNT(1) AS [A1]
        FROM   [dbo].[People] AS [Extent1]) AS [GroupBy1];

SELECT TOP (1) [Extent1].[ID]            AS [ID],
               [Extent1].[Name]          AS [Name],
               [Extent1].[Age]           AS [Age],
               [Extent1].[FavoriteColor] AS [FavoriteColor]
FROM   (SELECT [Extent1].[ID]                                  AS [ID],
               [Extent1].[Name]                                AS [Name],
               [Extent1].[Age]                                 AS [Age],
               [Extent1].[FavoriteColor]                       AS [FavoriteColor],
               row_number() OVER (ORDER BY [Extent1].[ID] ASC) AS [row_number]
        FROM   [dbo].[People] AS [Extent1]) AS [Extent1]
WHERE  [Extent1].[row_number] > 15
ORDER  BY [Extent1].[ID] ASC;

Guid

Method

private T getRandomEntityInPlace<T>(IGenericRepository<T> repo) {
    return repo.Items.OrderBy(o => Guid.NewGuid()).First();
}

Generated SQL

SELECT TOP (1) [Project1].[ID]            AS [ID],
               [Project1].[Name]          AS [Name],
               [Project1].[Age]           AS [Age],
               [Project1].[FavoriteColor] AS [FavoriteColor]
FROM   (SELECT NEWID()                   AS [C1],
               [Extent1].[ID]            AS [ID],
               [Extent1].[Name]          AS [Name],
               [Extent1].[Age]           AS [Age],
               [Extent1].[FavoriteColor] AS [FavoriteColor]
        FROM   [dbo].[People] AS [Extent1]) AS [Project1]
ORDER  BY [Project1].[C1] ASC

So in newer EF, you can again see that NewGuid is translated into SQL (as confirmed by @DrewNoakes https://stackoverflow.com/a/4120132/1037948). Even though both are "in-sql" methods, I'm guessing the Guid version is faster? If you didn't have to sort them in order to skip, and you could reasonably guess the amount to skip, then maybe the Skip method would be better.

Community
  • 1
  • 1
drzaus
  • 24,171
  • 16
  • 142
  • 201
0

Here is a nice way of doing this (mainly for people Googling).

You can also add .Take(n) on the end to only retrieve a set number.

model.CreateQuery<MyEntity>(   
    @"select value source.entity  
      from (select entity, SqlServer.NewID() as rand  
            from Products as entity 
            where entity.type == myTypeVar) as source  
            order by source.rand");
Jamie
  • 988
  • 1
  • 11
  • 19
0

How about this:


    var randomizer = new Random();
    var results = from en in context.MyEntity
                  where en.type == myTypeVar
                  let rand = randomizer.Next()
                  orderby rand
                  select en;

Klinger
  • 4,900
  • 1
  • 30
  • 35
  • I get a similar error to the Guid method in my edit: LINQ to Entities does not recognize the method 'Int32 Next()' method, and this method cannot be translated into a store expression.. – NikolaiDante Mar 17 '09 at 16:29
  • The AsEnumerable operator posted on Jon's answer should solve the issue. – Klinger Mar 17 '09 at 16:35
  • The following code works on my environment: var randomizer = new Random(); var result = context.MyEntity .Where(en => en.type == myTypeVar) .AsEnumerable() .OrderBy(en => randomizer.Next()); – Klinger Mar 17 '09 at 17:00
  • yes, it seems to work that way round, but not in the from en in context... format. – NikolaiDante Mar 17 '09 at 17:55
0

Toro's answer is the one I would use, but rather like this:

static IEnumerable<T> Randomize<T>(this IEnumerable<T> source)
{
  var list = source.ToList();
  var newList = new List<T>();

  while (source.Count > 0)
  {
     //choose random one and MOVE it from list to newList
  }

  return newList;
}
Migol
  • 8,161
  • 8
  • 47
  • 69
  • There's no need to create two lists - you can just swap elements in the list in a shuffle style way. This needs to be done with a bit of care, but it's better (IMO) than creating another copy for no reason. – Jon Skeet Mar 17 '09 at 17:14
  • You can, but it would make code less readable. IMHO this way is better, because it's more clear. Remember that we operate mainly on references, not values so there isn't much memory cost except for the list itself. – Migol Mar 18 '09 at 17:32
-1

Theoretically speaking (I haven't actually tried it yet), the following should do the trick :

Add a partial class to your context class :

public partial class MyDataContext{

        [Function(Name = "NEWID", IsComposable = true)] 
        public Guid Random()
        { 
            // you can put anything you want here, it makes no difference 
            throw new NotImplementedException();
        }
}

implementation :

from t in context.MyTable
orderby  context.Random()
select t; 
Segev -CJ- Shmueli
  • 1,535
  • 14
  • 15
  • Looks interesting, but I can't find a reference for `FunctionAttribute`. Do you mean `EdmFunction` or maybe `DbFunction` instead? – Drew Noakes Feb 18 '14 at 14:40