2017-11-09 8 views
1

の取り扱い...LINQの拡張メソッドとエラー私は、次のC#のコードを持っている

// We're essentially pivoting the data, using LINQ's GroupBy. 
    var pivotedOperands = Operands.GroupBy(o => new { outputid = (Guid)o[DETAILS_OUTPUTID], unitid = o[DETAILS_UNITID] }) 
     .Select(g => new 
     { 
      PivotKey = g.Key, 
      c1 = g.Where(x => (int)x[DETAILS_OV_SEQUENCEID] == 1).Sum(x => double.Parse(x[DETAILS_VALUE].ToString())), 
      r1 = g.Where(x => (int)x[DETAILS_OV_SEQUENCEID] == 2).Sum(x => double.Parse(x[DETAILS_VALUE].ToString())), 
      a1 = g.Where(x => (int)x[DETAILS_OV_SEQUENCEID] == 3).Sum(x => double.Parse(x[DETAILS_VALUE].ToString())) 
     }); 

それは(Listオブジェクトである)オペランドのデータを取り、ピボットを実行するためにGROUPBY()拡張メソッドを使用していますデータに基本的に、c1、r1およびa1は、シーケンスIDがそれぞれ1,2および3の異なるDataRowオブジェクトのすべての値です。 (私はそれが必要になった場合、もっと拘束することができますが、そうは思わないでしょう)

時にはc1の値が空であることがあります。 (これは想定されていませんが、時にはプロセスの上流でバグが発生しています)。c1が数値でない場合、double.Parse()コールは例外を発生させます。それはいいです。ここに私の問題がある。 Operandsオブジェクトに、たとえば3行にピボットされ、9つの値の1つが数値ではない9行が含まれている場合、例外を発生させたDataRowオブジェクトを特定できますか?

例: オペランドがSequenceIDと価値のために、以下の値が含まれている場合...

OutputID UnitID SequenceID Value 
A  1  1   '0' 
A  1  2   '0' 
A  1  3   '0' 
A  2  1   '' 
A  2  2   '0' 
A  2  3   '0' 
B  1  1   '0' 
B  1  2   '0' 
B  1  3   '0' 

...その後、我々はそれをしようとすると例外「入力文字列が正しい形式ではありませんでした」を取得します空の文字列をmyデータセットの4行目のdouble.Parse()メソッドで処理します。どの行が問題であるかをユーザーに伝えるフレンドリーな例外を出したい。このデータセットのどこかに問題があっただけではありません。例外の原因を正確に特定することは可能ですか?

Visual Studioで新しいC#コンソールアプリケーションを作成し、Mainメソッドに次のコードをダンプすると、問題を再現できます。あなたは情報が浮上し、あなたは故障の可能性を考慮するために、検索結果の種類を変更するか、またはあなたが例外に関するコンテキスト情報をキャプチャし、より多くの情報を持つ新しい例外をスローすることができますしたい方法に応じて

 // Create a DataTable so that we can easily create new DataRows to add to our List. 
     DataTable dt = new DataTable(); 
     DataColumn col = new DataColumn(); 
     col.DataType = System.Type.GetType("System.String"); 
     col.ColumnName = "OutputID"; 
     dt.Columns.Add(col); 

     col = new DataColumn(); 
     col.DataType = System.Type.GetType("System.Int32"); 
     col.ColumnName = "UnitID"; 
     dt.Columns.Add(col); 

     col = new DataColumn(); 
     col.DataType = System.Type.GetType("System.Int32"); 
     col.ColumnName = "SequenceID"; 
     dt.Columns.Add(col); 

     col = new DataColumn(); 
     col.DataType = System.Type.GetType("System.String"); 
     col.ColumnName = "Value"; 
     dt.Columns.Add(col); 



     // Create the List and add our sample data 
     List<DataRow> Operands = new List<DataRow>(); 

     DataRow dr = dt.NewRow(); 
     dr["OutputID"] = "A"; 
     dr["UnitID"] = "1"; 
     dr["SequenceID"] = 1; 
     dr["Value"] = "0"; 
     Operands.Add(dr); 

     dr = dt.NewRow(); 
     dr["OutputID"] = "A"; 
     dr["UnitID"] = "1"; 
     dr["SequenceID"] = 2; 
     dr["Value"] = "0"; 
     Operands.Add(dr); 

     dr = dt.NewRow(); 
     dr["OutputID"] = "A"; 
     dr["UnitID"] = "1"; 
     dr["SequenceID"] = 3; 
     dr["Value"] = "0"; 
     Operands.Add(dr); 

     dr = dt.NewRow(); 
     dr["OutputID"] = "A"; 
     dr["UnitID"] = "2"; 
     dr["SequenceID"] = 1; 
     dr["Value"] = "";  // This should cause an error. 
     Operands.Add(dr); 

     dr = dt.NewRow(); 
     dr["OutputID"] = "A"; 
     dr["UnitID"] = "2"; 
     dr["SequenceID"] = 2; 
     dr["Value"] = "0"; 
     Operands.Add(dr); 

     dr = dt.NewRow(); 
     dr["OutputID"] = "A"; 
     dr["UnitID"] = "2"; 
     dr["SequenceID"] = 3; 
     dr["Value"] = "0"; 
     Operands.Add(dr); 

     dr = dt.NewRow(); 
     dr["OutputID"] = "B"; 
     dr["UnitID"] = "1"; 
     dr["SequenceID"] = 1; 
     dr["Value"] = "0"; 
     Operands.Add(dr); 

     dr = dt.NewRow(); 
     dr["OutputID"] = "B"; 
     dr["UnitID"] = "1"; 
     dr["SequenceID"] = 2; 
     dr["Value"] = "0"; 
     Operands.Add(dr); 

     dr = dt.NewRow(); 
     dr["OutputID"] = "B"; 
     dr["UnitID"] = "1"; 
     dr["SequenceID"] = 3; 
     dr["Value"] = "0"; 
     Operands.Add(dr); 

     // Now pivot the data 
     try 
     { 
      var pivotedOperands = Operands.GroupBy(o => new { outputid = o[0], unitid = o[1] }) 
       .Select(g => new 
       { 
        PivotKey = g.Key, 
        c1 = g.Where(x => (int)x[2] == 1).Sum(x => double.Parse(x[3].ToString())), 
        r1 = g.Where(x => (int)x[2] == 2).Sum(x => double.Parse(x[3].ToString())), 
        a1 = g.Where(x => (int)x[2] == 3).Sum(x => double.Parse(x[3].ToString())) 
       }); 

      foreach (var o in pivotedOperands) 
      { 
       Console.WriteLine(string.Format("c1 = {0}; r1 = {1}; a1 = {2}", o.c1, o.r1, o.a1)); 
      } 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex.Message); 
     } 

     Console.WriteLine("Done."); 
     Console.ReadLine(); 
+1

をデフォルトに。 tryparse falseの場合は、インデックスを記録します。または、tryparse falseの場合、ゼロ(0) – Nkosi

答えて

2

その中に。

どちらの場合でも、ヘルパーメソッドを使用するのを恐れないでください。

string GetSumOrErrorMessage(int idToMatch, IEnumerable<DataRow> dataRow) 
{ 
    try 
    { 
     var sum = dataRow.Where(x => (int)x[2] == idToMatch).Sum(x => double.Parse(x[3].ToString())); 
     return sum.ToString(); 
    } 
    catch (Exception) 
    { 
     return "Error happened here"; // or something more specific 
    } 
} 

は今、あなたはこのようなあなたのクエリを変更することができます。たとえば、次のようなメソッドを作成することによって、あなたのセレクタに反復コードを処分したと仮定し

var pivotedOperands = Operands.GroupBy(o => new { outputid = o[0], unitid = o[1] }) 
     .Select(g => new 
     { 
      PivotKey = g.Key, 
      c1 = GetSumOrErrorMessage(1, g), 
      r1 = GetSumOrErrorMessage(2, g), 
      a1 = GetSumOrErrorMessage(3, g) 
     }); 

そして、あなたの出力は変身:

c1 = 0; r1 = 0; a1 = 0 
c1 = Error happened here; r1 = 0; a1 = 0 
c1 = 0; r1 = 0; a1 = 0 

あなたの場合、このパターンのように、というだけでを返すよりこれに役立つ特殊なMonadicタイプを調べることができます。たとえば、アクションが成功するとジェネリックな値を持ち、そうでないときはエラーメッセージを持つクラスを作成できます。 my CallMeMaybe libraryと同様に、さまざまな拡張メソッドとヘルパーを作成することで、値の解析を試みることができますが、解析が失敗した場合は空のMaybe<>を返します。 (例えば、Maybe.From(x[3].ToString()).ParseInt64().Select(i => i.ToString()).Else("Error happened here"))。また

、あなたが実際にあなたが悪いの入力を取得するときに停止するようにしたいが、それでも悪いの入力があった場所、あなたがキャッチして投げることができるかを知りたい場合は:

double GetSum(int idToMatch, IGrouping<object, DataRow> dataRows) 
{ 
    try 
    { 
     return dataRows.Where(x => (int)x[2] == idToMatch).Sum(x => double.Parse(x[3].ToString())); 
    } 
    catch (Exception e) 
    { 
     throw new Exception($"Failure when matching {idToMatch} with group {dataRows.Key}", e); 
    } 
} 

...

var pivotedOperands = Operands.GroupBy(o => new { outputid = o[0], unitid = o[1] }) 
     .Select(g => new 
     { 
      PivotKey = g.Key, 
      c1 = GetSum(1, g), 
      r1 = GetSum(2, g), 
      a1 = GetSum(3, g) 
     }); 

出力:

c1 = 0; r1 = 0; a1 = 0 
Failure when matching 1 with group { outputid = A, unitid = 2 } 
+2

がデフォルトになります。これは私には決して起こりませんでした。そして、ヘルパーメソッドを使用すると、より異常なケースを処理するための実行を十分に制御できます。ありがとうございました。 – DeadZone

1

例外を回避するには、TryParseを試してみてください。 TryParseがfalseの場合は、インデックスを持つselectを使用しても例外を回避するためにTryParseを使用してちょうど私の頭の上からゼロ(0

.Sum(x => { 
    double value = 0; 
    return double.TryParse(x[DETAILS_VALUE].ToString(), out value) ? value : 0; 
}) 
+0

ありがとうございました。 StriplingWarriorの答えは、解析に失敗した場合にデフォルト値を使用するか、例外を発生させて実行を中断させる柔軟性があるためです。 (これは私が必要とするものです) – DeadZone

+0

@DeadZoneそれはすべていいです。実際に例外をキャッチする場合は、これがより良い選択でした。助けてくれることをうれしく思います。 – Nkosi

関連する問題