2011-08-04 25 views
5

我需要在任何IDataReader的使用LINQ的實現這樣使用IDataReader作爲IEnumerable的最佳方法<T>?

var c = sqlDataReader.AsEnumerable().Count(); 

例子:

public abstract class Test 
{ 
    public abstract SqlDataReader GetSqlDataReader(); 

    public void Foo() 
    { 
     SqlDataReader sqlDataReader = GetSqlDataReader(); 
     IEnumerable<SqlDataReader> sqlEnumerable = sqlDataReader.AsEnumerable(); 
     var c = sqlEnumerable.Count(); 
     var s = sqlEnumerable.Sum(); 
     SqlDataReader first = sqlEnumerable.First(); 
     var t = first.GetSqlXml(10); 
    } 
} 

什麼是寫這個的最佳方式。 請寫下你的代碼片段。

回答

7

嘗試,這樣的:

public static class DataReaderExtension 
{ 
    public class EnumeratorWrapper<T> 
    { 
     private readonly Func<bool> moveNext; 
     private readonly Func<T> current; 

     public EnumeratorWrapper(Func<bool> moveNext, Func<T> current) 
     { 
      this.moveNext = moveNext; 
      this.current = current; 
     } 

     public EnumeratorWrapper<T> GetEnumerator() 
     { 
      return this; 
     } 

     public bool MoveNext() 
     { 
      return moveNext(); 
     } 

     public T Current 
     { 
      get { return current(); } 
     } 
    } 

    private static IEnumerable<T> BuildEnumerable<T>(
      Func<bool> moveNext, Func<T> current) 
    { 
     var po = new EnumeratorWrapper<T>(moveNext, current); 
     foreach (var s in po) 
      yield return s; 
    } 

    public static IEnumerable<T> AsEnumerable<T>(this T source) where T : IDataReader 
    { 
     return BuildEnumerable(source.Read,() => source); 
    } 
} 
+0

很棒的工作,謝謝 – b2Lord

8

您可以創建一個擴展方法來做到這一點(見下文警告)

public static class DataReaderExtension 
{ 
    public static IEnumerable<Object[]> AsEnumerable(this System.Data.IDataReader source) 
    { 
     if (source == null) 
      throw new ArgumentNullException("source"); 

     while (source.Read()) 
     { 
      Object[] row = new Object[source.FieldCount]; 
      source.GetValues(row); 
      yield return row; 
     } 
    } 
} 

在這裏找到:http://www.thinqlinq.com/default/Consuming-a-DataReader-with-LINQ.aspx


正如@LukeH指出,請注意,IDataReader的只支持讀取一次,轉發,你只能查詢一次枚舉。 (爲了解決這個問題,你可以調用ToList/ToArray,然後查詢)。

請注意SqlDataReader已經實現了IEnumerable,因此您不需要在給出的示例中執行此操作。

而且,要知道,它可能會更好做任何過濾/ aggrigating在服務器上(通過LINQ to SQL例如)

+0

請注意,這不會讓他們如前例所示,先執行'Count',後面跟着'Sum'。 (儘管如此,他們要麼需要重新查詢數據庫三次,要麼以某種方式緩衝結果,這兩者都有潛在的缺陷。) – LukeH

+0

好的編輯。 –

+0

@George Duckett 「請注意,SqlDataReader已經實現了IEnumerable,因此您不需要在給出的示例中執行此操作。」,是的,這是真的,但我需要IEnumerable 來使用例如方法「GetSqlXml」 – b2Lord

6

您可以使用此:

MyDataReader.Cast<IDataRecord>() 

但是,不要忘記在關閉DataReader之前執行linq語句。
使用ToList()例如

+0

+1對於ToList提示 – chillitom

2

你可以只加載DataReaderDataTable然後Select()

DataTable dt = new DataTable(); 
dt.Load(dataReader); 
DataRow[] rows = dt.Select();  //DataRow[] Implements IEnumerable 

IEnumerable<DataRow> rows = dt.AsEnumerable(); 
+0

嗨Gabriel。看來你有兩個帳戶(http://stackoverflow.com/users/5547620/gabrielk,http://stackoverflow.com/users/5547612/gabriel-kniznik)。如果你想鏈接它們,請按照以下步驟操作:http://stackoverflow.com/help/merging-accounts – Matt

4

這裏是我的兩分錢:

public static IEnumerable<T> Enumerate<T>(this T reader) where T: IDataReader 
{ 
    using(reader) 
     while(reader.Read()) 
     yield return reader; 
} 

public void Test() 
{ 
    var Res = 
    from Dr in MyDataReader.Enumerate() 
    select new { 
     ID = (Guid)Dr["ID"], 
     Description = Dr["Desc"] as string 
    }; 
} 

我覺得發佈這個的衝動,因爲使用後丟棄DataReader非常重要,沒有回答提及它。

這就是爲什麼我的實現在while循環周圍有一條using聲明。通過這種方式,我可以做「單手」查詢而不用擔心處置問題。

+1

對於你的代碼來說似乎沒問題,但對於更復雜的表格,可能會出現一個錯誤,它會停止迭代並永不處理讀者。我遇到了這個問題,我創建了一個「DisposableEnumerable」類,以便在Enumerate方法外完成使用,並在出現問題時調用Dispose。 – xxxo

+0

+1,用於提出關於'using'語句的建議以及關閉數據讀取器的重要性。在某些環境中,DataReader實際上可以「鎖定」數據庫,表,葉等。 – GoldBishop

0

我用下面的,但我喜歡@毛嗶嘰的建議更好,它提醒是什麼,我相信我用來做但後來忘記了IDataReader的實現

var reader = command.ExecuteReader(); 
var records = Enumerable 
    .Range(0, int.MaxValue) 
    .TakeWhile(i => reader.Read()) 
    .Select(i => reader as IDataRecord); 
相關問題