我需要在任何像这样的 IDataReader 实现上使用 Linq
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);
}
}
写这个的最好方法是什么? 请写下您的片段。
您可以使用这个:
MyDataReader.Cast<IDataRecord>()
但不要忘记在关闭 DataReader 之前执行 linq 语句。
例如使用 ToList()
试试这个:
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);
}
}
您可以创建一个扩展方法来执行此操作(请参阅下面的注意事项):
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,因此您不需要在给出的示例中执行此操作。
此外,请注意,最好在服务器上进行任何过滤/聚合(例如通过 LINQ to SQL)
这是我的两分钱:
public static IEnumerable<T> Enumerate<T>(this T reader) where T: IDataReader
{
while(reader.Read())
yield return reader;
}
public void Test()
{
using MyDataReader = GetDataReader();
var Res =
from Dr in MyDataReader.Enumerate()
select new {
ID = (Guid)Dr["ID"],
Description = Dr["Desc"] as string
};
}
我很想发帖,因为使用后处理
DataReader
非常重要,但没有答案提到它。
这就是为什么我的实现在
using
循环周围有一个 while
语句。通过这种方式,我可以进行“一只手”查询,而不必担心DataReader
处置。
您可以将
DataReader
加载到 DataTable
中,然后 Select()
:
DataTable dt = new DataTable();
dt.Load(dataReader);
DataRow[] rows = dt.Select(); //DataRow[] Implements IEnumerable
或
IEnumerable<DataRow> rows = dt.AsEnumerable();
我使用了以下内容,但我更喜欢@Serge的建议,它让我想起了我相信我曾经做过的事情,但后来忘记了 IDataReader 实现了
var reader = command.ExecuteReader();
var records = Enumerable
.Range(0, int.MaxValue)
.TakeWhile(i => reader.Read())
.Select(i => reader as IDataRecord);
今天我实现了以下通用解决方案,因为我们不喜欢依赖任何第三方依赖项或任何复杂的解决方案。
使用反射 + lambda 表达式进行简单快速的
IDataReader
到IEnumerable<T>
转换
public IEnumerable<T> ConvertToEnumerable<T>(SqlDataReader dr) where T : class, new()
{
List<string> lstColumns = Enumerable.Range(0, dr.FieldCount).Select(dr.GetName).ToList();
List<PropertyInfo> lstProperties = typeof(T).GetProperties().Where(x => lstColumns.Contains(x.Name, StringComparer.OrdinalIgnoreCase)).ToList();
while (dr.Read())
{
var entity = new T();
lstProperties.Where(w => dr[w.Name] != System.DBNull.Value).ToList().ForEach(i => i.SetValue(entity, dr[i.Name], null));
yield return entity;
}
}
用途
SqlDataReader dr;
...
var result = ConvertToEnumerable<Foo>(dr).FirstOrDefault();
Or
var result = ConvertToEnumerable<Foo>(dr).ToList();