2017-11-04 5 views
2

私は生産ライン用にDBを使用しています。 Ordersテーブル、Ordertrackerテーブル、Itemテーブル、Itemtrackerテーブルがあります。ToList()を呼び出さずにIQueryableで反復処理します。

注文とアイテムの両方にステータスが多対多の関係を持っています。トラッカーテーブルは、アイテムがトラッカー内に複数のエントリを持つことができるような方法でこれらの関係を解決します。

は、私は物事をより明確にするために、テーブルの写真をアップロードしようとしたが、残念ながら、私はまだ十分なポイントを持っていない:C

は、私は、その最後のステータスItemtrackerテーブルの条件を満たしている項目を見つける必要があり、 '3'または '0'のいずれかです。

次に、これらの項目の最初の項目を取得する必要があります。次のように私はこれを達成するために使用しています

手順は次のとおりです。

  1. は、特定のステータスを持つすべての注文を取得します。
  2. その注文のすべてのアイテムを入手してください。
  3. 最後のステータスが0または3であるアイテムをすべて取得します。
  4. これらのアイテムの最初の項目を取得してください。

私のコードは次のとおりです。

public ITEM GetFirstItemFailedOrNotInProductionFromCurrentOrder() 
    { 

     var firstOrder = GetFirstOrderInProductionAndNotCompleted(); 

     var items = ERPContext.ITEM.Where(i => i.OrderID == firstOrder.OrderID) as IQueryable<ITEM>; 

     if (CheckStatusOfItems(items) != null) 
     { 
      var nextItem = CheckStatusOfItems(items); 

      return nextItem ; 
     } 
     else 
     { 
      return null; 
     } 
    } 

    private ITEM CheckStatusOfItems(IQueryable<ITEM> items) 
    { 
     List<ITEM> listOfItemsToProduce = new List<ITEM>(); 

     foreach (ITEM item in items.ToList()) 
     { 

      var lastStatusOfItem = ERPContext.ITEMTRACKER.Where(it => it.ItemID == item.ItemID) 
                 .OrderByDescending(it => it.ItemTrackerID).FirstOrDefault(); 

      if (lastStatusOfItem.ItemStatus == (int)ItemStatus.Failed || lastStatusOfItem.ItemStatus == (int)ItemStatus.Confirmed) 
      { 
       listOfItemsToProduce.Add(item); 
      } 
     } 
     return listOfItemsToProduce.FirstOrDefault(); 

    } 

を、これはすべて正常に動作し、私は必要なものを返しますが、私はこれが最善のアプローチではないかもしれないことに注意してくださいね。今のところ、IQueryableのアイテムのコレクションは6つ以上のアイテムを含むことはありませんが、それが大きくなる場合は、IQueryableでToList()を呼び出し、メモリ内の結果を反復処理することはおそらく良い考えではありません。

IQueryableアイテムを繰り返し処理して、ToList()を呼び出しずに結果を参照することなく、特定のステータスを最新のステータスとして持つアイテムをフェッチする方法がありますか?

アドバイスをいただければ幸いです。

+0

は超スローになります怠惰な情報を読み込む。各ループは新しいクエリになります。あなたのモデルで 'ITEMTRACKER'は' ITEM'と関連付けられていませんか? –

+0

Btwの場合、 'CheckStatusOfItems'の結果を変数にキャッシュし、その変数にnullをチェックする必要があります。それ以外の場合は、2回呼び出してクエリを2回実行します。それは、最初のメソッド全体を 'var firstOrder = ...; var items = ...;戻り値CheckStatusOfItems(items); 'ヌルでない場合は結果を返します。ヌルであればヌルを返します。結果は(ヌル)結果を返すものと同じです – pinkfloydx33

+0

はい、そうです、ITEMTRACKERはITEM表をITEMSTATUS表に結合する表。 ItemIDとOrderIDはITEMのPKです。 ItemID、OrderID、およびItemStatusはITEMTRACKERのFKです.PKはItemTrackerIDです。どのようにこれに取り組むための任意の提案?ありがとう – Chi

答えて

4

LINQクエリ構文を使用すると、命令的な繰り返しを記述したのとほぼ同じように、単一のクエリを宣言的に構築できます。 foreachwhereletiffromvarに変換:

private ITEM CheckStatusOfItems(IQueryable<ITEM> items) 
{ 
    var query = 
     from item in items 
     let lastStatusOfItem = ERPContext.ITEMTRACKER 
      .Where(it => it.ItemID == item.ItemID) 
      .OrderByDescending(it => it.ItemTrackerID) 
      .FirstOrDefault() 
     where (lastStatusOfItem.ItemStatus == (int)ItemStatus.Failed || lastStatusOfItem.ItemStatus == (int)ItemStatus.Confirmed) 
     select item; 

    return query.FirstOrDefault(); 
} 

または代わりfrom代わりのletTake(1)代わりのFirstOrDefault()を使用して:あなたがしている場合のIQueryableをループ

private ITEM CheckStatusOfItems(IQueryable<ITEM> items) 
{ 
    var query = 
     from item in items 
     from lastStatusOfItem in ERPContext.ITEMTRACKER 
      .Where(it => it.ItemID == item.ItemID) 
      .OrderByDescending(it => it.ItemTrackerID) 
      .Take(1) 
     where (lastStatusOfItem.ItemStatus == (int)ItemStatus.Failed || lastStatusOfItem.ItemStatus == (int)ItemStatus.Confirmed) 
     select item; 

    return query.FirstOrDefault(); 
} 
+1

'Take(1)'は解決策です。 'FirstOrDefault()'は常に問題を引き起こします。 –

+0

華麗!ありがとうございます - それを正しく理解すれば、TakeコレクションのToList()メソッドではなく式ツリーとしてクエリを実行することができます。 – Chi

+0

あなたは大歓迎です。どちらの方法も、単一のSQLクエリ(SELECT TOP 1 ...)を生成して実行します。重要な点は、すべてのものを可能な限り( 'query'変数のように)' IQueryable 'にして、最後に一度だけ実行することです。 –

関連する問題