我们有很多数据层代码都遵循这种非常普遍的模式:
public DataTable GetSomeData(string filter)
{
string sql = "SELECT * FROM [SomeTable] WHERE SomeColumn= @Filter";
DataTable result = new DataTable();
using (SqlConnection cn = new SqlConnection(GetConnectionString()))
using (SqlCommand cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.Add("@Filter", SqlDbType.NVarChar, 255).Value = filter;
result.Load(cmd.ExecuteReader());
}
return result;
}
我认为我们可以做得更好。我现在的主要提示是它强制将所有记录加载到内存中,即使是大型集合也是如此。我希望能够利用 DataReader 一次只在 ram 中保留一条记录的能力,但如果我直接返回 DataReader,则在离开 using block 时连接会被切断。
我如何改进它以允许一次返回一行?
最佳答案
再一次,我对问题的想法揭示了答案。具体来说,我写的最后一句“一次一行”。我意识到我真的不在乎它是一个数据读取器,只要我可以逐行枚举它即可。这让我想到了这一点:
public IEnumerable<IDataRecord> GetSomeData(string filter)
{
string sql = "SELECT * FROM [SomeTable] WHERE SomeColumn= @Filter";
using (SqlConnection cn = new SqlConnection(GetConnectionString()))
using (SqlCommand cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.Add("@Filter", SqlDbType.NVarChar, 255).Value = filter;
cn.Open();
using (IDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
yield return (IDataRecord)rdr;
}
}
}
}
一旦我们迁移到 3.5 并且可以开始在结果上使用其他 linq 运算符,这将工作得更好,我喜欢它,因为它让我们开始考虑每层之间的“管道”,用于返回的查询很多结果。
缺点是持有多个结果集的读者会很尴尬,但这种情况极为罕见。
更新
自从我在 2009 年开始使用这种模式以来,我了解到最好也将其设为通用 IEnumerable<T>
返回类型并添加 Func<IDataRecord, T>
参数将 DataReader 状态转换为循环中的业务对象。否则,惰性迭代可能会出现问题,这样您每次都会看到查询中的最后一个对象。
关于c# - 在 Using 语句中从 DataLayer 返回 DataReader,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/850065/