40

如何以随机顺序返回匹配的实体?
需要明确的是,这是实体框架的东西和 LINQ to Entities。

(航空代码)

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

谢谢

编辑:
我尝试将此添加到上下文中:

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

并使用此查询:

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

但我得到了这个错误:

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

编辑(当前代码):

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

12 回答 12

53

执行此操作的一种简单方法是订购,Guid.NewGuid()然后订购发生在客户端。您也许可以说服 EF 在服务器端做一些随机的事情,但这并不一定简单——而且使用“按随机数排序”来做这件事显然是有问题的。

要在 .NET 端而不是在 EF 中进行排序,您需要AsEnumerable

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

最好在列表中获取无序版本,然后再随机播放。

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>

洗牌比排序更有效,除此之外。有关获取适当实例的详细信息,请参阅我关于随机性的文章。RandomStack Overflow 上有很多 Fisher-Yates shuffle 实现。

于 2009-03-17T16:08:01.097 回答
39

Jon 的回答很有帮助,但实际上您可以让数据库使用Guid和 Linq to Entities 进行排序(至少,您可以在 EF4 中):

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

这会生成类似于以下的 SQL:

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

在我的测试中,使用Take(10)生成的查询(在 SQL 中转换为TOP 10),该查询针对具有 1,794,785 行的表始终在 0.42 到 0.46 秒之间运行。不知道 SQL Server 是否对此进行了任何类型的优化,或者它是否为该表中的每一行生成了一个 GUID。无论哪种方式,这都比将所有这些行带入我的流程并尝试在那里对其进行排序要快得多。

于 2010-11-07T22:49:26.453 回答
30

简单的解决方案是创建一个数组(或 a List<T>),然后随机化它的索引。

编辑:

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

编辑:就个人而言,我发现 Jon Skeet 的答案更优雅:

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

当然,您可以使用随机数生成器而不是Guid.NewGuid().

于 2009-03-17T16:09:38.173 回答
4

不幸的是,在服务器端对它进行排序的NewGuidhack 会导致实体在连接(或急切获取包含)的情况下重复。

请参阅有关此问题的此问题。

为了克服这个问题,您可以在某些唯一值计算的服务器端使用而不是NewGuidsql checksum,在客户端计算一次随机种子来随机化它。请参阅对先前链接问题的回答。

于 2015-07-06T12:31:37.850 回答
2

此处提供的解决方案在客户端上执行。如果你想要在服务器上执行的东西,这里有一个 LINQ to SQL 的解决方案,你可以将它转换为实体框架。

于 2009-03-24T15:16:51.940 回答
1

lolo_house 有一个非常简洁、简单和通用的解决方案。您只需将代码放在单独的静态类中即可使其工作。

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;
        }
    }
}

然后使用代码只是做:

var randomizeQuery = Query.Randomize();

很简单!谢谢lolo_house。

于 2014-06-26T05:56:13.987 回答
0

这个怎么样:


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

于 2009-03-17T16:25:54.717 回答
0

Toro 的答案是我会使用的答案,但就像这样:

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;
}
于 2009-03-17T17:07:33.353 回答
0

这是一个很好的方法(主要用于谷歌搜索)。

您还可以在末尾添加 .Take(n) 以仅检索一组数字。

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");
于 2010-10-07T01:09:41.843 回答
0

我认为最好不要在类中添加属性。更好地使用该位置:

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;
    }

并且调用将(如 toList() 或 toArray()):

var 结果 = IEnumerable.Where(..).Randomize();

于 2013-04-03T12:50:57.403 回答
0

(来自 EF Code First 的交叉发布:如何获取随机行

比较两个选项:


跳过(随机行数)

方法

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();
}
  • 需要 2 个查询

生成的 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;

指导

方法

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

生成的 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

因此,在较新的 EF 中,您可以再次看到它NewGuid被翻译成 SQL(由 @DrewNoakes https://stackoverflow.com/a/4120132/1037948确认)。即使两者都是“in-sql”方法,我猜 Guid 版本更快?如果您不必为了跳过而对它们进行排序,并且您可以合理地猜测要跳过的数量,那么 Skip 方法可能会更好。

于 2013-06-14T16:29:36.983 回答
-1

从理论上讲(我还没有真正尝试过),以下应该可以解决问题:

将部分类添加到您的上下文类:

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();
        }
}

执行 :

from t in context.MyTable
orderby  context.Random()
select t; 
于 2013-01-01T08:02:08.400 回答