在Using语句中从DataLayer返回DataReader

Joe*_*orn 10 .net c# database .net-2.0

我们有很多数据层代码遵循这个非常通用的模式:

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;
}
Run Code Online (Sandbox Code Playgroud)

我想我们可以做得更好一点.我现在的主要抱怨是它强制将所有记录加载到内存中,即使对于大型集合也是如此.我希望能够利用DataReader的能力,一次只能在ram中保留一条记录,但如果我直接返回DataReader,则在离开using块时会切断连接.

如何改进这一点以允许一次返回一行?

Joe*_*orn 13

再次,为问题撰写我的想法的行为揭示了答案.具体来说,我写的"一次一行"的最后一句话.我意识到我并不在乎它是一个datareader,只要我可以逐行枚举它.这导致我这样:

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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

一旦我们移动到3.5并且可以开始在结果上使用其他linq运算符,这将更好地工作,我喜欢它,因为它让我们开始考虑每个层之间的"管道",以便返回大量的查询结果.

缺点是对于持有多个结果集的读者来说会很尴尬,但这种情况非常罕见.

更新
自2009年我第一次开始使用此模式以来,我了解到最好是将其设置为泛型IEnumerable<T>返回类型并添加Func<IDataRecord, T>参数以将DataReader状态转换为循环中的业务对象.否则,延迟迭代可能会出现问题,因此您每次都会在查询中看到最后一个对象.

  • 与使用firehose游标填充DataTable的原始代码相比,此解决方案将保持连接打开,直到您迭代整个结果集,并尽快关闭连接.实际上,您正在为另一个(数据库连接)交换一个资源(内存),不确定这是否是您的问题. (3认同)

Hen*_*man 7

你想要的是一个支持的模式,你必须使用

cmd.ExecuteReader(CommandBehavior.CloseConnection);
Run Code Online (Sandbox Code Playgroud)

并从using()GetSomeData()方法中删除它们.呼叫者必须提供例外安全保障,以确保读者能够关闭.