[英]Return datareader from method
我有以下方法
public static SqlDataReader MenuDataReader(string url)
{
using (SqlConnection con = new SqlConnection(connectionString))
{
using (SqlCommand cmd = new SqlCommand("spR_GetChildMenus", con))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@PageUrl", url);
cmd.Parameters.AddWithValue("@MenuId", ParameterDirection.Output);
cmd.Parameters.AddWithValue("@ParentId", ParameterDirection.Output);
cmd.Parameters.AddWithValue("@TitleText", ParameterDirection.Output);
cmd.Parameters.AddWithValue("@ExternalUrl", ParameterDirection.Output);
cmd.Parameters.AddWithValue("@FullUrl", ParameterDirection.Output);
cmd.Parameters.AddWithValue("@ChildCount", ParameterDirection.Output);
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
if (reader.HasRows)
{
//return reader;
while (reader.Read())
{
return reader;
}
}
}
}
return null;
}
我这样称呼
SqlDataReader reader = MenuDataReader(url);
if (reader.HasRows)
{
while (reader.Read())
{ }}
但是我收到错误消息
阅读器关闭时调用 HasRows 的尝试无效。
谁能帮我吗
谢谢
如https://msdn.microsoft.com/zh-cn/library/system.data.sqlclient.sqlcommand(v=vs.110).aspx中所示 :
public static SqlDataReader ExecuteReader(String connectionString, String commandText,
CommandType commandType, params SqlParameter[] parameters) {
SqlConnection conn = new SqlConnection(connectionString);
using (SqlCommand cmd = new SqlCommand(commandText, conn)) {
cmd.CommandType = commandType;
cmd.Parameters.AddRange(parameters);
conn.Open();
// When using CommandBehavior.CloseConnection, the connection will be closed when the
// IDataReader is closed.
SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
return reader;
}
}
您是否真的需要阅读器,还是只需要某种方法来遍历其中的行? 我建议一个迭代器块 。 您可以将行遍历源法里面, yield
依次调用者的每一行。
这项技术有一个缺点:因为每次迭代都会产生相同的对象,所以在某些情况下这可能会导致问题,因此最好也请委托在某个地方复制该行的内容。 我还喜欢将其抽象为可用于任何查询的通用方法,并使用相同的委托技术来处理参数数据,如下所示:
private IEnumerable<T> GetRows<T>(string sql, Action<SqlParameterCollection> addParameters, Func<IDataRecord, T> copyRow)
{
using (var cn = new SqlConnection("Connection string here"))
using (var cmd = new SqlCommand(sql, cn)
{
cmd.CommandType = CommandType.StoredProcedure;
addParameters(cmd.Parameters);
cn.Open();
using (var rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
yield return copyRow(rdr);
}
rdr.Close();
}
}
}
public IEnumerable<MenuItem> GetChildMenus(string url)
{
return GetRows<MenuItem>("spR_GetChildMenus", p =>
{
//these lines are copied from your question, but they're almost certainly wrong
p.AddWithValue("@PageUrl", url);
p.AddWithValue("@MenuId", ParameterDirection.Output);
p.AddWithValue("@ParentId", ParameterDirection.Output);
p.AddWithValue("@TitleText", ParameterDirection.Output);
p.AddWithValue("@ExternalUrl", ParameterDirection.Output);
p.AddWithValue("@FullUrl", ParameterDirection.Output);
p.AddWithValue("@ChildCount", ParameterDirection.Output);
}, r =>
{
return new MenuItem( ... );
}
}
我不会退还阅读器- Dispose
您的连接和命令将关闭连接。 相反,我将返回您数据的代表性模型。
当您在using
语句内部返回时,代码在SqlConnection
上调用Dispose
。 这将关闭DataReader
,从而导致错误。
由Danan's answer下的一个问题触发,这里是一个基于抽象的解决方案。 此外,它使用良好的实践,如使用声明、异步编程(为简洁起见省略了取消标记)和适当的对象处理(特别是关于连接)。
// Example invocation
public async Task DemonstrateUsage()
{
var query = @"SELECT * FROM Order WHERE Id = @Id;";
var parameters = new Dictionary<string, object>()
{
["@Id"] = 1,
};
// Caller disposes the reader, which disposes the connection too
await using var reader = await this.ExecuteReader(this.CreateConnection, query, parameters);
while (await reader.ReadAsync())
Console.Log("And another row!");
}
// Concrete implementation of how we produce connections
private DbConnection CreateConnection()
{
return new SqlConnection("ConnectionString");
}
// Fully abstract solution
private async Task<DbDataReader> ExecuteReader(Func<DbConnection> connectionFactory,
string query, IReadOnlyDictionary<string, object> parameters,
CommandType commandType = CommandType.Text)
{
var connection = connectionFactory();
try
{
await using var command = connection.CreateCommand();
command.CommandType = commandType;
command.CommandText = query;
foreach (var pair in parameters)
{
var parameter = command.CreateParameter();
parameter.ParameterName = pair.Key;
parameter.Value = pair.Value;
command.Parameters.Add(parameter);
}
await connection.OpenAsync();
return await command.ExecuteReaderAsync(CommandBehavior.CloseConnection);
}
catch
{
// We have failed to return a disposable reader that can close the connection
// We must clean up by ourselves
await connection.DisposeAsync();
throw;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.