C#IEnumerator/收益率结构可能不好?

C#IEnumerator/收益率结构可能不好?,c#,.net,database,resources,yield,C#,.net,Database,Resources,Yield,背景:我从数据库中得到了一堆字符串,我想返回它们。传统上是这样的: public List<string> GetStuff(string connectionString) { List<string> categoryList = new List<string>(); using (SqlConnection sqlConnection = new SqlConnection(connectionString)) {

背景:我从数据库中得到了一堆字符串,我想返回它们。传统上是这样的:

public List<string> GetStuff(string connectionString)
{
    List<string> categoryList = new List<string>();
    using (SqlConnection sqlConnection = new SqlConnection(connectionString))
    {
        string commandText = "GetStuff";
        using (SqlCommand sqlCommand = new SqlCommand(commandText, sqlConnection))
        {
            sqlCommand.CommandType = CommandType.StoredProcedure;

            sqlConnection.Open();
            SqlDataReader sqlDataReader = sqlCommand.ExecuteReader();
            while (sqlDataReader.Read())
            {
                categoryList.Add(sqlDataReader["myImportantColumn"].ToString());
            }
        }
    }
    return categoryList;
}
public IEnumerable<string> GetStuff(string connectionString)
{
    using (SqlConnection sqlConnection = new SqlConnection(connectionString))
    {
        string commandText = "GetStuff";
        using (SqlCommand sqlCommand = new SqlCommand(commandText, sqlConnection))
        {
            sqlCommand.CommandType = CommandType.StoredProcedure;

            sqlConnection.Open();
            SqlDataReader sqlDataReader = sqlCommand.ExecuteReader();
            while (sqlDataReader.Read())
            {
                yield return sqlDataReader["myImportantColumn"].ToString();
            }
        }
    }
}
public IEnumerable<string> GetStuff(string connectionString)
{
    DataTable table = new DataTable();
    using (SqlConnection sqlConnection = new SqlConnection(connectionString))
    {
        string commandText = "GetStuff";
        using (SqlCommand sqlCommand = new SqlCommand(commandText, sqlConnection))
        {
            sqlCommand.CommandType = CommandType.StoredProcedure;
            SqlDataAdapter dataAdapter = new SqlDataAdapter(sqlCommand);
            dataAdapter.Fill(table);
        }

    }
    foreach(DataRow row in table.Rows)
    {
        yield return row["myImportantColumn"].ToString();
    }
}
public List GetStuff(字符串连接字符串)
{
List categoryList=新列表();
使用(SqlConnection SqlConnection=newsqlconnection(connectionString))
{
string commandText=“GetStuff”;
使用(SqlCommand SqlCommand=newsqlcommand(commandText,sqlConnection))
{
sqlCommand.CommandType=CommandType.StoredProcess;
sqlConnection.Open();
SqlDataReader SqlDataReader=sqlCommand.ExecuteReader();
while(sqlDataReader.Read())
{
Add(sqlDataReader[“myImportantColumn”].ToString());
}
}
}
返回类别列表;
}
但是我想消费者会想反复浏览这些项目,而不关心其他的事情,我不想把自己放在一个列表中,所以如果我返回一个IEnumerable,一切都是好的/灵活的。所以我想我可以使用“收益率-回报率”类型的设计来处理这件事……类似这样的事情:

public List<string> GetStuff(string connectionString)
{
    List<string> categoryList = new List<string>();
    using (SqlConnection sqlConnection = new SqlConnection(connectionString))
    {
        string commandText = "GetStuff";
        using (SqlCommand sqlCommand = new SqlCommand(commandText, sqlConnection))
        {
            sqlCommand.CommandType = CommandType.StoredProcedure;

            sqlConnection.Open();
            SqlDataReader sqlDataReader = sqlCommand.ExecuteReader();
            while (sqlDataReader.Read())
            {
                categoryList.Add(sqlDataReader["myImportantColumn"].ToString());
            }
        }
    }
    return categoryList;
}
public IEnumerable<string> GetStuff(string connectionString)
{
    using (SqlConnection sqlConnection = new SqlConnection(connectionString))
    {
        string commandText = "GetStuff";
        using (SqlCommand sqlCommand = new SqlCommand(commandText, sqlConnection))
        {
            sqlCommand.CommandType = CommandType.StoredProcedure;

            sqlConnection.Open();
            SqlDataReader sqlDataReader = sqlCommand.ExecuteReader();
            while (sqlDataReader.Read())
            {
                yield return sqlDataReader["myImportantColumn"].ToString();
            }
        }
    }
}
public IEnumerable<string> GetStuff(string connectionString)
{
    DataTable table = new DataTable();
    using (SqlConnection sqlConnection = new SqlConnection(connectionString))
    {
        string commandText = "GetStuff";
        using (SqlCommand sqlCommand = new SqlCommand(commandText, sqlConnection))
        {
            sqlCommand.CommandType = CommandType.StoredProcedure;
            SqlDataAdapter dataAdapter = new SqlDataAdapter(sqlCommand);
            dataAdapter.Fill(table);
        }

    }
    foreach(DataRow row in table.Rows)
    {
        yield return row["myImportantColumn"].ToString();
    }
}
public IEnumerable GetStuff(字符串连接字符串)
{
使用(SqlConnection SqlConnection=newsqlconnection(connectionString))
{
string commandText=“GetStuff”;
使用(SqlCommand SqlCommand=newsqlcommand(commandText,sqlConnection))
{
sqlCommand.CommandType=CommandType.StoredProcess;
sqlConnection.Open();
SqlDataReader SqlDataReader=sqlCommand.ExecuteReader();
while(sqlDataReader.Read())
{
产生返回sqlDataReader[“myImportantColumn”].ToString();
}
}
}
}
但是现在我读了更多关于收益率的内容(在像这样的网站上……msdn似乎没有提到这一点),它显然是一个懒惰的评估器,它保持着填充器的状态,期望有人请求下一个值,然后只运行它,直到它返回下一个值

在大多数情况下,这似乎很好,但对于DB调用,这听起来有点冒险。作为一个有点做作的例子,如果有人从我正在通过DB调用填充的IEnumerable中请求一个IEnumerable,完成了它的一半,然后陷入了一个循环中……就我所知,我的DB连接将永远保持打开状态


在某些情况下,如果迭代器没有完成,听起来像是在自找麻烦……我遗漏了什么吗?

您没有遗漏任何东西。您的示例显示了如何不使用收益率回报。将项目添加到列表中,关闭连接,然后返回列表。您的方法签名仍然可以返回IEnumerable


编辑:尽管如此,Jon有一个观点(太令人惊讶了!):从性能角度来看,流媒体实际上是最好的选择。毕竟,如果我们在这里讨论的是100000(1000000?10000000?)行,您不希望首先将这些行加载到内存中。

不,您走的是正确的道路。。。产量将锁定读者。。。您可以在调用IEnumerable时进行另一个数据库调用来测试它。这里不要使用yield。您的示例很好。

这是一种平衡行为:您是希望立即将所有数据强制放入内存,以便释放连接,还是希望以占用连接时间为代价从数据流中获益

在我看来,这一决定可能取决于打电话的人,他们知道更多他们想做什么。如果使用迭代器块编写代码,调用方可以非常轻松地将该流形式转换为完全缓冲形式:

List<string> stuff = new List<string>(GetStuff(connectionString));
List stuff=新列表(GetStuff(connectionString));
另一方面,如果您自己进行缓冲,则调用者无法返回到流模型

因此,我可能会使用流模型,并在文档中明确说明它的作用,并建议调用者做出适当的决定。您甚至可能希望提供一个助手方法来基本上调用流化版本并将其转换为列表

当然,如果您不相信您的呼叫者会做出适当的决定,并且您有充分的理由相信他们永远不会真正想要流式传输数据(例如,无论如何它不会返回太多),那么请使用列表方法。不管是哪种方式,都要记录它——它很可能会影响返回值的使用方式


当然,处理大量数据的另一个选择是使用批处理,当然,这是有点偏离原来的问题,但在流媒体通常具有吸引力的情况下,这是一种不同的考虑方式。SQL数据库查询不像文件那样易于流化。相反,只查询您认为需要的内容,并将其作为您想要的任何容器返回(
IList
DataTable
,等等)
IEnumerable
在这里对您没有帮助。

您可以使用SqlDataAdapter代替,并填充数据表。大概是这样的:

public List<string> GetStuff(string connectionString)
{
    List<string> categoryList = new List<string>();
    using (SqlConnection sqlConnection = new SqlConnection(connectionString))
    {
        string commandText = "GetStuff";
        using (SqlCommand sqlCommand = new SqlCommand(commandText, sqlConnection))
        {
            sqlCommand.CommandType = CommandType.StoredProcedure;

            sqlConnection.Open();
            SqlDataReader sqlDataReader = sqlCommand.ExecuteReader();
            while (sqlDataReader.Read())
            {
                categoryList.Add(sqlDataReader["myImportantColumn"].ToString());
            }
        }
    }
    return categoryList;
}
public IEnumerable<string> GetStuff(string connectionString)
{
    using (SqlConnection sqlConnection = new SqlConnection(connectionString))
    {
        string commandText = "GetStuff";
        using (SqlCommand sqlCommand = new SqlCommand(commandText, sqlConnection))
        {
            sqlCommand.CommandType = CommandType.StoredProcedure;

            sqlConnection.Open();
            SqlDataReader sqlDataReader = sqlCommand.ExecuteReader();
            while (sqlDataReader.Read())
            {
                yield return sqlDataReader["myImportantColumn"].ToString();
            }
        }
    }
}
public IEnumerable<string> GetStuff(string connectionString)
{
    DataTable table = new DataTable();
    using (SqlConnection sqlConnection = new SqlConnection(connectionString))
    {
        string commandText = "GetStuff";
        using (SqlCommand sqlCommand = new SqlCommand(commandText, sqlConnection))
        {
            sqlCommand.CommandType = CommandType.StoredProcedure;
            SqlDataAdapter dataAdapter = new SqlDataAdapter(sqlCommand);
            dataAdapter.Fill(table);
        }

    }
    foreach(DataRow row in table.Rows)
    {
        yield return row["myImportantColumn"].ToString();
    }
}
public IEnumerable GetStuff(字符串连接字符串)
{
DataTable=新的DataTable();
使用(SqlConnection SqlConnection=newsqlconnection(connectionString))
{
string commandText=“GetStuff”;
使用(SqlCommand SqlCommand=newsqlcommand(commandText,sqlConnection))
{
sqlCommand.CommandType=CommandType.StoredProcess;
SqlDataAdapter dataAdapter=新的SqlDataAdapter(sqlCommand);
dataAdapter.Fill(表);
}
}
foreach(table.Rows中的DataRow行)
{
收益返回行[“myImportantColumn”].ToString();
}
}
这样,您可以一次查询所有内容,并立即关闭连接,但仍然在懒洋洋地迭代结果。此外,此m的调用方