我从昨天起就一直试图让这种方法起作用,并且已经把头发拉出来了 -
基本上,我有一个从数据库中检索文章的方法。以下是方法代码:
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方法......如果需要我可以发布更多代码,但我认为我已经拥有了必要的一切。如果您有任何想法,请告诉我。
答案 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);
}