2009-03-17 7 views
3

LINQを使用してかなり大きなデータセットを生成する必要がある場合は、しばらく時間がかかる場合があります。 )% '年齢の使用に関するフィードバックを生成しますが、これを行う簡単な方法がありますか?大規模データセットでLINQ式を実行しているときの進捗状況を報告するには

var pairs = from car in A 
      from truck in B 
      where car.color==truck.color 
      select new {car, truck}; 

例は、私が1000台のトラックで1000台の車とリストBを持つリストAを持っていると私はcar.color == truck.colorリンクこのすべての可能な注文(車、トラック)のペアを選択するようにしたいと言います

これは、ある時点でネストされたforeachループのセットとして評価されます。私はそれが介入し、理想的にはプログレスバーまたは何かを更新するので、完了年数%を報告できるようにしたいと思います。

編集:私はこれを実行していますので、私はこれを行う

mPairs = pairs.ToList(); 

:ちょうど私のクエリの後、私は(実行するクエリを強制的に)このようなリストとしてメンバ変数に結果を格納しますUIスレッドが要求に応じてLINQ式を評価するときに、UIスレッドがフリーズするのをやめたいからです(これはSilverlight BTWにあります)。ですから、私は進捗状況を報告したいのですが。 UXは、基本的にこれは:

  1. ユーザがワークスペース
  2. 上にアイテムをドラッグエンジンは、ワークスペース上の他の項目のすべてに(多くの)接続の可能性を決定するために、バックグラウンドスレッド上に蹴ります。
  3. エンジンが計算中に新しい接続が許可されず、新しい項目が他の項目に「接続可能」になる(LINQ経由で未使用の接続パスがすべて決定された)ことを示すレポートが表示されます。
  4. エンジンが計算(クエリ)を完了すると、その項目はUIで接続可能であり、将来の使用のために可能な接続パスがローカル変数に格納されます(たとえば、ユーザーがアイテムを接続するためにクリックすると、 (同様のプロセスは、項目の削除で発生しなければならない)、それを添加した場合

)を算出したものに基づいて、ハイライト

答えて

2

私がよく使用したものは、生成されたアイテムの数を返すDataContextのアダプタでした。

public class ProgressArgs : EventArgs 
{ 
    public ProgressArgs(int count) 
    { 
     this.Count = count; 
    } 

    public int Count { get; private set; } 
} 

public class ProgressContext<T> : IEnumerable<T> 
{ 
    private IEnumerable<T> source; 

    public ProgressContext(IEnumerable<T> source) 
    { 
     this.source = source; 
    } 

    public event EventHandler<ProgressArgs> UpdateProgress; 

    protected virtual void OnUpdateProgress(int count) 
    { 
     EventHandler<ProgressArgs> handler = this.UpdateProgress; 
     if (handler != null) 
      handler(this, new ProgressArgs(count)); 
    } 

    public IEnumerator<T> GetEnumerator() 
    { 
     int count = 0; 
     foreach (var item in source) 
     { 
      // The yield holds execution until the next iteration, 
      // so trigger the update event first. 
      OnUpdateProgress(++count); 
      yield return item; 
     } 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 
} 

使用

var context = new ProgressContext(
    from car in A 
    from truck in B 
    select new {car, truck}; 
); 
context.UpdateProgress += (sender, e) => 
{ 
    // Do your update here 
}; 

var query = from item in context 
      where item.car.color==item.truck.color; 

// This will trigger the updates 
query.ToArray(); 

は唯一の問題は、あなたが合計数を知っている限り、あなたが簡単にパーセンテージを行うことができないです。総計を行うには、多くの場合、リスト全体を処理する必要があり、コストがかかることがあります。事前に合計数を把握している場合は、UpdateProgressイベントハンドラでパーセンテージを計算することができます。

+0

私はこのアプローチが本当に好きです。私のLINQ式はいくつかのネストされたfromから成り立っています - これは少なくとも最外部のリストの周りに置くことができます(少なくとも頻繁な更新のオーバーヘッドが発生しないように、おそらくすべて必要です)。 – caryden

4

EDIT:クエリ式は、中括弧を許可していないので、これは現在動作しません。編集...あなたは常に進歩を示した「ノーオペレーション」を選択または句追加することができ

public class ProgressCounter 
{ 
    private readonly int total; 
    private int count; 
    private int lastPercentage; 

    public ProgressCounter(int total) 
    { 
     this.total = total; 
    } 

    public void Update() 
    { 
     count++; 
     int currentPercentage = (count * 100)/total; 
     if (currentPercentage != lastPercentage) 
     { 
      Console.WriteLine("Done {0}%", currentPercentage); 
      lastPercentage = currentPercentage; 
     } 
     return true; 
    } 
} 

... 

var progressCounter = new ProgressCounter(A.Count * B.Count); 

var pairs = from car in A 
      from truck in B 
      where progressCounter.Update() 
      where car.color==truck.color 
      select new {car, truck}; 

は常に厄介である、副作用の使用に注意してください。

このような演算子をMoreLINQに追加すると、Pipe、Apply、Viaなどと呼ばれることがありました。

1

Linqのほとんどは遅延評価を使用して行われます。したがって、結果をforeachするまで、実際にはクエリは実行されません。ペアから結果を取得するたびに、クエリの一部が評価されます。

つまり、結果を反復処理するforeachループにprogresを表示することができます。欠点は、結果セットの大きさを数えることで、結果セットを繰り返し実行してクエリを実行することを事前に知ることができないことです。

0

彼のアプローチがはるかに簡潔であると確信していますが、私の鉱山はJon'sと似ていました。同じ手段で何かを一緒にハックすることができます。

var pairs = from car in A 
      from truck in B 
      let myProgress = UpdateProgress(...) 
      where car.color == truck.color 
      select new { car, truck }; 

private int UpdateProgress(...) 
{ 
    Console.WriteLine("Updating Progress..."); 
    return -1; 
} 

前述のように、クエリは反復されるまで実行されませんが。 これは、クエリ内に新しいスコープ変数を作成するという追加の欠点としても機能します。

関連する問題