4

状況:を持っていList<IQueryable<MyDataStructure>>ます。それぞれに対して単一のlinqクエリを並行して実行し、結果を結合したいと考えています。

質問:パラメータとして渡すことができる linq クエリを作成するにはどうすればよいですか?

コード例:

ここにいくつかの単純化されたコードがあります。まず、次のコレクションがありIQueryable<string>ます。

    public List<IQueryable<string>> GetQueries()
    {
        var set1 = (new List<string> { "hello", "hey" }).AsQueryable();
        var set2 = (new List<string> { "cat", "dog", "house" }).AsQueryable();
        var set3 = (new List<string> { "cat", "dog", "house" }).AsQueryable();
        var set4 = (new List<string> { "hello", "hey" }).AsQueryable();

        var sets = new List<IQueryable<string>> { set1, set2, set3, set4 };

        return sets;
    }

文字「h」で始まるすべての単語を検索したいと思います。単一のIQueryable<string>場合、これは簡単です:

query.Where(x => x.StartsWith("h")).ToList()

IQueryable<string>しかし、すべてのオブジェクトに対して同じクエリを並行して実行し、結果を結合したいと考えています。これを行う1つの方法は次のとおりです。

        var result = new ConcurrentBag<string>();
        Parallel.ForEach(queries, query =>
        {
            var partOfResult = query.Where(x => x.StartsWith("h")).ToList();

            foreach (var word in partOfResult)
            {
                result.Add(word);
            }
        });

        Console.WriteLine(result.Count);

しかし、私はこれをより一般的な解決策にしたいと考えています。linq 操作を個別に定義し、それをパラメーターとしてメソッドに渡すことができるようにします。このようなもの:

        var query = Where(x => x.FirstName.StartsWith("d") && x.IsRemoved == false)
            .Select(x => x.FirstName)
            .OrderBy(x => x.FirstName);

        var queries = GetQueries();

        var result = Run(queries, query);

しかし、私はこれを行う方法に途方に暮れています。何か案は?

4

2 に答える 2

5

最初に-現在の実装を考えると、使用する理由はありませんIQueryable<T>-単に使用できますIEnumerable<T>.

IEnumerable<IEnumerable<T>>次に、とを受け取るメソッドを記述してFunc<IEnumerable<T>, IEnumerable<U>>、結果を作成できます。

IEnumerable<IEnumerable<U>> QueryMultiple<T,U>(IEnumerable<IEnumerable<T>> inputs, Func<IEnumerable<T>,IEnumerable<U>> mapping)
{
     return inputs.AsParallel().Select(i => mapping(i));
}

これを次のように使用できます。

void Run()
{
    IEnumerable<IEnumerable<YourType>> inputs = GetYourObjects();

    Func<IEnumerable<YourType>, IEnumerable<YourType>> query = i => 
       i.Where(x => x.FirstName.StartsWith("d") && x.IsRemoved == false)
        .Select(x => x.FirstName)
        .OrderBy(x => x.FirstName);

    var results = QueryMultiple(inputs, query);
}
于 2013-02-07T19:00:38.547 に答える
5

したがって、最初に必要だったのは、一連のクエリを取得し、それらすべてを実行して、フラット化された結果のリストを取得する方法でした。それはとても簡単です:

public static IEnumerable<T> Foo<T>(IEnumerable<IQueryable<T>> queries)
{
    return queries.AsParallel()
            .Select(query => query.ToList())
            .SelectMany(results => results);
}

クエリごとにそれを実行 (呼び出しToList) し、 のおかげで並列に実行されAsParallel、結果は を介し​​て 1 つのシーケンスにフラット化されますSelectMany

もう 1 つは、一連のクエリの各クエリに多数のクエリ操作を追加することでした。これは並列化する必要はなく (遅延実行のおかげでWhereOrderBy、 などの呼び出しにはほとんど時間がかかりません)、次の方法で実行できますSelect

var queries = GetQueries().Select(query =>
    query.Where(x => x.FirstName.StartsWith("d")
        && !x.IsRemoved)
    .Select(x => x.FirstName)
    .OrderBy(x => x.FirstName));

var results = Foo(queries);

個人的には、これら 2 つの方法を組み合わせる必要はないと思います。両方を行うメソッドを作成することはできますが、実際にはかなり別個の概念であるため、その必要はないと思います。ただし、それらを組み合わせたい場合は、次のとおりです。

public static IEnumerable<TResult> Bar<TSource, TResult>(
    IEnumerable<IQueryable<TSource>> queries,
    Func<IQueryable<TSource>, IQueryable<TResult>> selector)
{

    return queries.Select(selector)
        .AsParallel()
        .Select(query => query.ToList())
        .SelectMany(results => results);
}

Foo必要に応じて、またはBar拡張メソッドを自由に作成してください。また、それらを使用する場合は、名前をより適切なものに変更することをお勧めします

于 2013-02-07T19:05:47.613 に答える