【问题标题】:Method not getting called correctly when passing Func<T1, T2> as argument将 Func<T1, T2> 作为参数传递时未正确调用方法
【发布时间】:2012-12-07 04:02:31
【问题描述】:

从昨天开始,我一直在尝试让这种方法发挥作用,并且已经开始拔掉我的头发 -

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

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 方法...如果需要,我可以发布更多代码,但我认为我已经拥有了一切必要的东西。如果您有任何想法,请告诉我。

【问题讨论】:

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


    【解决方案1】:

    您返回的是迭代器方法的结果,而不是枚举它。 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;
    }
    

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

    【讨论】:

    • 确认这是正确答案。连接字符串异常被抛出,因为它不再有状态。调试起来非常混乱。
    【解决方案2】:

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


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

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

    【讨论】:

      猜你喜欢
      • 2015-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多