2013-02-07 5 views
4

상황 : 나는 List<IQueryable<MyDataStructure>>입니다. 나는 각각에 대한 단일 linq 쿼리를 병렬로 실행 한 다음 결과를 조인하려고합니다.병렬로 여러 IQueryable에서 동일한 linq 쿼리를 실행 하시겠습니까?

질문 : 매개 변수로 전달할 수있는 linq 쿼리를 만드는 방법은 무엇입니까?

예제 코드 :

는 여기에 몇 가지 간단한 코드입니다. 내가 편지 'H'로 시작하는 모든 단어를 찾을 싶습니다

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

: 첫째, IQueryable<string>의 컬렉션이 있습니다. 하나의 IQueryable<string>이 쉽다 :

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

하지만 병렬로 모든 IQueryable<string> 객체에 대해 동일한 쿼리를 실행 한 다음 결과를 결합하려는. 이를 수행하는 한 가지 방법이 있습니다.

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

하지만 어떻게해야합니까? 어떤 아이디어?

답변

5

을 그래서 제일 먼저 그 당신 원하는 것은 일련의 쿼리를 실행하고 모든 쿼리를 실행 한 다음 병합 된 결과 목록을 가져 오는 것입니다. 우리가 그것을 (그것에 ToList 전화) 실행 각 쿼리

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

그것은 AsParallel에 병렬로 감사를 수행, 그리고 그 결과가 SelectMany을 통해 하나의 시퀀스로 평평하게되어 그만큼 간단합니다.

다른 작업은 일련의 쿼리에서 각 쿼리에 많은 쿼리 작업을 추가하는 것입니다. 이 병렬화 할 필요가 없습니다 (지연된 실행 덕분에, 통화 등 Where, OrderBy, 거의 시간이 걸릴 없음) 단지 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); 

정말 표시되지 않습니다 이 두 가지 방법을 결합해야합니다. 두 가지를 모두 수행하는 메소드를 만들 수는 있지만 실제로는 별개의 개념이므로 필요는 없습니다.당신이 그 (것)들을 결합 할 경우, 그래도 여기있다 :

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 확장 방법을 주시기 바랍니다. 또한, 더 나은 이름을 바꾸려면 더 잘 사용할 수 있습니다.

4

우선 현재 구현 된 경우 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); 
} 
+0

이것은 흥미 롭습니다.하지만이 질문과 쿼리 사용에 대한 일반적인 생각은 ** 평가/구문 분석/처리 될 표현 트리를 구축/생성하여 공급자에 대한 호출을 줄이는 것이 었습니다. 당신이 on/against를 실행하고있는 provider; 그런 식으로 공급자는 그 (아마도 복잡한) 표현을 취할 수 있고 그것이 가장 최적이라고 결정하는 어떤 방식 으로든 한 번 실행 할 수 있습니다.이 코드 샘플은이를 수행합니까? –

+0

@BrettCaswell OP 코드는 모두 LINQ to Objects이며 메모리에서 처리되므로 문제가되지 않습니다. 이것은 메모리에서 작동하지만 - 그렇게 처리하지는 않습니다. –

+0

답장을 보내 주셔서 감사합니다. 당신이 맞다고 생각합니다. SQL, Entity 또는 Object인지 여부에 관계없이 생각했습니다. 밑줄 개념은 여전히 ​​표현식 트리를 작성하는 것입니다. PredicateBuilders와 LINQkit을 살펴 봤는데, 그것은 ExpressionExpression을 생성하기 위해 InvocationExpressions (invoke)를 사용하는 Expression.Lambda 을 생성하는 것처럼 보입니다. EntitySet 확장 메소드로 약간의 속임수가 있습니다. –