2011-03-29 47 views
5

我打電話給多個結果集(總是2)並將結果寫入單獨文件(以管道分隔格式)的存儲過程。我無法將結果集分成單獨存儲的特效。我正在使用IDataReader和IEnumerable在內存中保持儘可能少的內容。更好的方式來使用IEnumerable <IEnumerable <string>>

是否有比使用GetEnumerator/MoveNext/Current去內部IEnumerable<string>傳遞給File.AppendAllLines更簡單的方法來使用我的IEnumerable<IEnumerable<string>>

public void Execute() 
    { 
     var reader = GetLines(); 

     using (var enumerator = reader.GetEnumerator()) 
     { 
      enumerator.MoveNext(); 

      File.AppendAllLines("file1.dat", enumerator.Current); 
      enumerator.MoveNext(); 

      File.AppendAllLines("file2.dat", enumerator.Current); 
     } 
    } 

    public IEnumerable<IEnumerable<string>> GetLines() 
    { 
     Database db = DatabaseFactory.CreateDatabase("connectionStringKey"); 
     using (var command = db.GetStoredProcCommand("getdata_sp")) 
     { 
      var reader = db.ExecuteReader(command); 
      yield return GetInnerEnumerable(reader); 
      reader.NextResult(); 
      yield return GetInnerEnumerable(reader); 
     } 
    } 

    private IEnumerable<string> GetInnerEnumerable(IDataReader reader) 
    { 
     while (reader.Read()) 
     { 
      object[] rowValues = new object[reader.FieldCount]; 
      reader.GetValues(rowValues); 
      yield return String.Join("|", rowValues); 
     } 
    } 
+0

也許我不清楚命名方法GetLines() - 它返回IEnumerable >。應該叫它GetReaders() – foson 2011-03-29 20:40:14

回答

5

爲什麼不是foreach循環?這是最基本的。

+0

我需要傳入文件名 – foson 2011-03-29 20:14:09

+0

仔細看看代碼。他將不得不寫兩個foreach,其中一個採取所有不平衡的計數,另一個則是非常重要的。 – skarmats 2011-03-29 20:14:55

4

就個人而言,我只想用一個foreach循環與跟蹤一個獨立的變量寫到哪個文件,是這樣的:

public void Execute() 
{ 
    var reader = GetLines(); 

    int i = 0; 
    foreach (var inner in reader) 
    { 
     if (i % 2 == 0) 
      File.AppendAllLines("file1.dat", inner); 
     else 
      File.AppendAllLines("file2.dat", inner); 
     ++i; 
    } 
} 
+0

謝謝。我知道這是主觀的,但我沒有看到一個foreach和一個計數器比GetEnumerator/MoveNext/Current方法更清晰/更容易理解。我想我希望有一個「流行」/「下一個」擴展方法(基本上是一個枚舉器),也許是來自Rx的新東西。 – foson 2011-03-29 20:50:48

2

也許轉的GetLines()結果到一個數組中,並通過索引訪問它(既然你說過總會有2個結果集)?

public void Execute() 
{ 
    IEnumerable<string>[] rows = GetLines().ToArray(); 

    File.AppendAllLines("file1.dat", rows[0]); 
    File.AppendAllLines("file2.dat", rows[1]); 
} 
+0

好主意,但是當我運行它時,它不像預期那樣工作。由於在調用.ToArray()時會調用NextResult,所以當迭代第一個Enumerable(rows [0])時,我們從行獲得結果[1] – foson 2011-03-29 20:27:22

+0

@foson:啊,我沒有考慮到(我忘記了關於出現在收益率返回之間的'NextResult()')。 – BoltClock 2011-03-29 20:29:48

4

您可以使用SelectMany()來枚舉枚舉,因爲您只對值本身感興趣。

編輯:

按照評論SelectMany()不適合給定的用例,所以最好是使用foreach循環:

var reader = GetLines(); 
int index = 0; 
foreach(var lines in reader) 
    File.AppendAllLines(string.Format("file{0}.dat", index++%2 + 1), lines); 
+0

行將進入單獨的文件,所以我不認爲我可以扁平 – foson 2011-03-29 20:32:23

+0

@foson:你是對的,更新我的回答 – BrokenGlass 2011-03-29 20:45:43

1

的foreach是隱含IEnumerable的支持。所以:

public void Execute() 
{ 
    var reader = GetLines(); 

    using (var enumerator = reader.GetEnumerator()) 
    { 
     enumerator.MoveNext(); 

     File.AppendAllLines("file1.dat", enumerator.Current); 
     enumerator.MoveNext(); 

     File.AppendAllLines("file2.dat", enumerator.Current); 
    } 
} 

變爲:

public void Execute() 
{ 
    var reader = GetLines(); 

    int index = 0; 

    foreach (string line in reader) 
    { 
     if ((index % 2) == 0) 
      File.AppendAllLines("file1.dat", line); 

     else 
      File.AppendAllLines("file2.dat", line); 

     index++; 
    } 
} 

或者:

public void Execute() 
{ 
    var reader = GetLines(); 

    var evenLines = reader.Where((str, i) => i % 2 == 0); 
    var oddLines = reader.Where((str, i) => i % 2 != 0); 

    foreach (string line in evenLines) 
     File.AppendAllLines("file1.dat", line); 

    foreach (string line in oddLines) 
     File.AppendAllLines("file2.dat", line); 
} 
+0

2 foreach的原因sp被執行兩次 – foson 2011-03-29 20:43:06

2

我只想改變我GetLines方法如下

public IEnumerable<string> GetLines() 
{ 
    Database db = DatabaseFactory.CreateDatabase("connectionStringKey"); 
    using (var command = db.GetStoredProcCommand("getdata_sp")) 
    { 
     var reader = db.ExecuteReader(command); 
     for (var i = 0; i < 2; i++) 
     { 
      foreach(var cur in GetInnerEnumerable(reader)) 
      { 
      yield return cur; 
      } 
      reader.NextResult(); 
     } 
    } 
} 

讓它返回一個IEnumerable<IEnumerable<string>>瓦特虐待給API的消費者帶來不必要的負擔。我的猜測是,他們只會更願意將此視爲IEnumerable<string>

+0

這只是一些內部應用程序代碼,沒有消耗通過任何其他代碼。我怎麼知道我的第一個閱讀器已經結束,我應該開始將結果寫入第二個文件? – foson 2011-03-29 20:53:42

+0

@foson你的問題明確表示只有2 – JaredPar 2011-03-29 20:55:38

+0

正確,但Execute方法如何知道來自GetLines()的哪些值來自第一個讀取器,並保存在第一個文件中,哪些來自第二個讀取器? – foson 2011-03-30 03:18:44

0

你可以壓縮你的結果與你的所有文件名,Tuple<>像這樣:

using System.Linq; 
using FileZip = System.Tuple< 
    System.String, 
    System.Collections.Generic.IEnumerable< 
     System.String>>; 

public void Execute() 
{ 
    var files = new string[] { "file1.dat", "file2.dat" }; 
    var results = GetLines(); 

    foreach (var file in files.Zip(results, (f, r) => new FileZip(f, r))) 
    { 
     File.AppendAllLines(file.Item1, file.Item2); 
    } 
} 

當然,我敢肯定,這是怎麼回事,你返回不同的行數,以儘快拋出,但它會做你想要的。

相關問題