Linq to Entities,随机顺序

Nik*_*nte 36 c# linq-to-entities entity-framework

如何以随机顺序返回匹配的实体?
要明确这是Entity Framework的东西和LINQ to Entities.

(航空代码)

IEnumerable<MyEntity> results = from en in context.MyEntity
                                where en.type == myTypeVar
                                orderby ?????
                                select en;
Run Code Online (Sandbox Code Playgroud)

谢谢

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

public Guid Random()
{
    return new Guid();
}
Run Code Online (Sandbox Code Playgroud)

并使用此查询:

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

但我得到了这个错误:

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

编辑(当前代码):

IEnumerable<MyEntity> results = (from en in context.MyEntity
                                 where en.type == myTypeVar
                                 orderby context.Random()
                                 select en).AsEnumerable();
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 50

这样做的一种简单方法是按顺序排序,Guid.NewGuid()然后在客户端进行排序.你可能能够说服EF在服务器端做一些随机的事情,但这并不一定简单 - 使用"随机数排序"这样做显然已被打破.

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

IEnumerable<MyEntity> results = context.MyEntity
                                       .Where(en => en.type == myTypeVar)
                                       .AsEnumerable()
                                       .OrderBy(en => context.Random());
Run Code Online (Sandbox Code Playgroud)

最好将无序版本放入列表然后随机播放.

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>
Run Code Online (Sandbox Code Playgroud)

除了其他任何东西之外,洗牌比排序更有效.有关获取适当实例的详细信息,请参阅我的随机性文章Random.Stack Overflow上有很多Fisher-Yates shuffle实现.

  • 卫生署.AsEnumerable. (2认同)

Dre*_*kes 37

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

from e in MyEntities
orderby Guid.NewGuid()
select e
Run Code Online (Sandbox Code Playgroud)

这会生成类似于的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
Run Code Online (Sandbox Code Playgroud)

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


Mic*_*tov 26

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

编辑:

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

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

var results = from ... in ... where ... orderby Guid.NewGuid() select ...
Run Code Online (Sandbox Code Playgroud)

当然,你可以采用随机数发生器代替Guid.NewGuid().