一致するエンティティをランダムな順序で返すにはどうすればよいですか?
明確にする必要があるのは、これがEntity Frameworkのものと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();
簡単な解決策は、配列(または_List<T>
_)を作成し、そのインデックスをランダム化することです。
編集:
_static IEnumerable<T> Randomize<T>(this IEnumerable<T> source) {
var array = source.ToArray();
// randomize indexes (several approaches are possible)
return array;
}
_
編集:個人的に、私はジョン・スキートの答えがよりエレガントだと思います:
_var results = from ... in ... where ... orderby Guid.NewGuid() select ...
_
そして確かに、Guid.NewGuid()
の代わりに乱数ジェネレータを使用できます。
これを行う簡単な方法は、Guid.NewGuid()
で順序付けすることですが、順序付けはクライアント側で行われます。サーバー側でランダムに何かを行うようにEFを説得できるかもしれませんが、それは必ずしも単純ではありません-「ランダムな順序による順序付け」を使用してそれを行う 明らかに壊れている 。
EFではなく.NET側で順序付けを行うには、AsEnumerable
が必要です。
IEnumerable<MyEntity> results = context.MyEntity
.Where(en => en.type == myTypeVar)
.AsEnumerable()
.OrderBy(en => context.Random());
unorderedバージョンをリストで取得し、それをシャッフルすることをお勧めします。
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>
何よりも、シャッフルはソートよりも効率的です。ただし、適切なRandom
インスタンスを取得する方法の詳細については、私の ランダム性に関する記事 を参照してください。 Stack Overflowで利用可能なFisher-Yatesシャッフル実装はたくさんあります。
ジョンの答えは役に立ちますが、実際にはcanGuid
とLinq to Entitiesを使用してDBに順序付けを行わせます(少なくとも、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 for everyの行をテーブルに生成したのか)はわかりません。どちらの方法でも、これらすべての行を私のプロセスに取り込み、そこで並べ替えを試みます。
ここで提供されるソリューションはクライアントで実行されます。サーバー上で実行するものが必要な場合、 これはLINQ to SQLのソリューションです エンティティフレームワークに変換できます。
トロの答えは私が使うものですが、むしろ次のようになります:
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;
}
(クロスポスト EFコードを最初に:ランダムな行を取得する方法 )
2つのオプションの比較:
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();
}
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();
}
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 で確認されています)。どちらも「SQL内」メソッドですが、Guidバージョンの方が速いと思いますか?スキップするためにそれらを並べ替える必要がなく、スキップする量を合理的に推測できる場合は、Skipメソッドの方が良いでしょう。
これはこれを行うための素晴らしい方法です(主にグーグルの人々のために)。
末尾に.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");
これはどう:
var randomizer = new Random();
var results = from en in context.MyEntity
where en.type == myTypeVar
let Rand = randomizer.Next()
orderby Rand
select en;
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に感謝します。
理論的に言えば(まだ実際に試したことはありません)、次の方法でうまくいくはずです。
部分クラスをコンテキストクラスに追加します。
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;