3

从昨天开始,我一直在尝试使这种方法起作用,并开始拔头发-

基本上,我有一个从数据库中检索文章的方法。下面是方法代码:

public static IEnumerable<Article> RetrieveTopTenArticles()
{
    using(var dbConn = new SqlConnection(Settings.Instance.DbConnectionString))
    {
        const String query = "SELECT TOP 10 Title, Content FROM Article";
        Func<SqlDataReader, Article> operation = reader => reader.ToArticle();
        return dbConn.SqlRetrieveMultiple(query, operation);
    }
}

在一个单独的“扩展”类中 -

public static IEnumerable<T> SqlRetrieveMultiple<T>(this SqlConnection sqlConnection, String query, Func<SqlDataReader, T> operation, params SqlParameter[] parameters)
{
    sqlConnection.Open();
    using (var command = new SqlCommand(query, sqlConnection))
    {
        if (parameters.Length > 0)
        {
            command.Parameters.AddRange(parameters);
        }
        using (SqlDataReader reader = command.ExecuteReader())
        {
            while (reader.Read())
            {
                yield return operation.Invoke(reader);
            }
        }
    }
}

如您所见,我有一个扩展方法,它执行 SQL 选择语句,并迭代结果,调用操作并将每个 SqlDataReader 行转换为我的文章类型(也使用扩展方法完成)。我相信我过去使用过这种完全相同的扩展方法,没有任何问题。

当我调试它并到达 dbConn.SqlRetrieveMultiple(query, operation); 行,如果我按 F11 进入,它会完全跨过该行。它继续沿着结束嵌套括号链走,最终我在 sqlConnection.Open() 上得到一个异常。

我得到的例外是:

ConnectionString 属性尚未初始化。

我发现如果我删除我的 Func 委托,该功能将正常运行。我觉得这必须是一些愚蠢的/与扩展方法或 SqlConnection 无关的东西,因为如果我将它放在同一个类中的常规私有静态方法中,我会得到相同的行为。如果我在 sqlConnection.Open(); 处打开 .NET 框架源单步执行;行,它跳转到 .NET 框架的 ctor 方法……如果需要,我可以发布更多代码,但我认为我已经拥有了一切必要的东西。如果您有任何想法,请告诉我。

4

2 回答 2

4

您正在返回迭代器方法的结果,而不是枚举它。迭代器方法使用延迟执行。结果,直到它要使用的连接被释放之后,它才真正做任何事情。这就是为什么它“只是跳过”该方法以及为什么它在以后的某个任意时间失败的原因。

您可以通过两种方式解决此问题。要么提前获取所有结果并返回它们,在处理发生之前急切地获取项目,使用类似 ToArray 的方法:

return dbConn.SqlRetrieveMultiple(query, operation).ToArray();

或者您可以通过将外部方法也设为迭代器方法来确保将处理延迟到延迟检索项目之后:

using(var dbConn = new SqlConnection(Settings.Instance.DbConnectionString))
{
    const String query = "SELECT TOP 10 Title, Content FROM Article";
    Func<SqlDataReader, Article> operation = reader => reader.ToArticle();
    foreach (var e in dbConn.SqlRetrieveMultiple(query, operation))
        yield return e;
}

但是,请记住,如果用户重复这个惰性版本两次,他们最终会打开两个连接。这可能是也可能不是你想要的。

于 2012-12-07T04:08:22.953 回答
1

给出的异常意味着您的连接字符串有问题。发表它。


我也会这样使用你的函数:

while (reader.Read())
{
    yield return operation(reader);
}
于 2012-12-07T04:04:34.300 回答