传递Func <t1,t2 =“”>作为参数</t1,>时,方法无法正确调用

时间:2012-12-07 04:02:31

标签: c# exception-handling ado.net extension-methods deferred-execution

我从昨天起就一直试图让这种方法起作用,并且已经把头发拉出来了 -

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

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 select语句的扩展方法,并迭代结果,调用操作并将每个SqlDataReader行转换为我的Article类型(也使用扩展方法)。我相信我过去使用过这种完全相同的扩展方法,没有任何问题。

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

我得到的例外是:

  

ConnectionString属性尚未初始化。

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

2 个答案:

答案 0 :(得分:4)

您将返回迭代器方法的结果而不是枚举它。 Iterator methods use deferred execution。结果,它实际上并没有做任何事情,直到它将使用的连接已被处置。这就是为什么它“刚刚跨过”这个方法以及为什么它在以后的某个任意时间失败了。

您可以通过两种方式解决此问题。要么提前获得所有结果并返回它们,要在处理之前急切地获取项目,使用类似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;
}

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

答案 1 :(得分:1)

给出的例外意味着连接字符串出现问题。发布它。


我也会这样使用你的功能:

while (reader.Read())
{
    yield return operation(reader);
}