Linq以实体,随机顺序
 如何以随机顺序返回匹配实体? 
 只是要清楚这是entity framework的东西和LINQ到实体。 
(空码)
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(); 
	
 简单的解决scheme是创build一个数组(或一个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() 。 
 这样做的一个简单的方法是通过Guid.NewGuid()进行sorting,然后在客户端进行sorting。 你可能会说服EF在服务器端做一些随机的事情,但这不一定是简单的 – 用“按随机数sorting”这个做法显然已经失效了 。 
 为了在.NET中而不是在EF中进行sorting,您需要使用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> 
 除了其他任何东西外,洗牌比分类更有效率。 尽pipe如此,请参阅我的关于获取适当的Random实例的细节的随机性文章 。  Stack Overflow上有许多Fisher-Yates shuffle实现。 
 乔恩的答案是有帮助的,但实际上你可以让数据库使用Guid和Linq实体进行sorting(至less,你可以在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 
 在我的testing中,对结果查询使用Take(10) (在SQL中转换为TOP 10 ),查询在包含1,794,785行的表的0.42和0.46秒之间运行一致。 不知道SQL Server是否对此进行了任何优化,或者是否为该表中的每一行生成了一个GUID。 无论哪种方式,这将比将所有这些行放入我的过程并尝试在那里sorting要快得多。 
这里提供的解决scheme在客户端执行。 如果你想要在服务器上执行的东西, 这里是一个LINQ to SQL的解决scheme ,你可以转换成entity framework。
  NewGuid hack对服务器端进行sorting不幸的是导致实体在连接的情况下被重复(或者渴望获取包括)。 
看到这个问题关于这个问题。
 为了克服这个问题,你可以使用一个随机的种子来代替NewGuid在一些唯一值计算服务器端进行一个sql checksum ,一个客户端计算一个随机种子来随机化它。 看到我以前的链接问题的答案 。 
这个怎么样:
var randomizer = new Random(); var results = from en in context.MyEntity where en.type == myTypeVar let rand = randomizer.Next() orderby rand select en;var randomizer = new Random(); var results = from en in context.MyEntity where en.type == myTypeVar let rand = randomizer.Next() orderby rand select en;
托罗的答案是我会使用的答案,而是这样的:
 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; } 
这是一个很好的做法(主要是为人们谷歌search)。
您也可以在结尾添加.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"); 
从理论上说(我还没有真正尝试过),下面应该做的诀窍:
为您的上下文类添加一个部分类:
 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; 
(从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; 
GUID
方法
 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版本更快? 如果您不必为了跳过而对它们进行sorting,并且可以合理地猜测跳过的数量,那么跳过方法可能会更好。 
lolo_house有一个非常简洁,通用的解决scheme。 你只需要把代码放在一个单独的静态类中就可以工作。
 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。
我认为最好不要给课堂添加属性。 最好使用这个位置:
 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 result = IEnumerable.Where(..)。Randomize();