当reader使用ado.net包装器类关闭时,调用HasRows的尝试无效
本文关键字:调用 HasRows 无效 ado 使用 reader net 包装 | 更新日期: 2023-09-27 18:11:08
我正在尝试为ado.net编写包装器类,问题是我得到以下错误,我不知道如何解决它。
我读过很多关于这个的问题,但是没有一个使用包装器类,因此没有帮助。
谁能指出我在正确的方向或提供与相同的问题的链接,所以我可以阅读如何解决它。还是我完全走错了路?
public List<LoginDetails> Authenticate(string id)
{
const string spName = "dbo.MemberLogin";
List<SqlParameter> parameters = new List<SqlParameter> { new SqlParameter("@Username", id) };
var rdr = _iAdoCommandWrapper.ExecuteDataReaderAsync(DbConnectionAbstractClass.ConnectionString, CommandType.StoredProcedure, spName, parameters.ToArray());
var data = new List<LoginDetails>();
if (rdr.HasRows)
{
while (rdr.Read())
{
data.Add(new LoginDetails
{
UserName = (string)rdr["MemberUsername"],
Password = (string)rdr["MemberPassword"],
MemberId = (string)rdr["MemberID"],
Role = (string)rdr["MemberRole"]
});
}
//-------
rdr.NextResult();
while (rdr.Read())
{
data.Add(new LoginDetails
{
RolesForMember = (string)(rdr["MembersRoles"])
});
}
//----------
}
return data.ToList();// rowsAffected.Result;
}
包装器
public SqlDataReader ExecuteDataReaderAsync(string connectionString, CommandType cmdType, string spName, params SqlParameter[] cmdParameters)
{
//TODO make async once fixed problem
using (var conn = new SqlConnection(connectionString))
{
using (var cmd = new SqlCommand(spName, conn))
{
cmd.CommandType = cmdType;
cmd.Parameters.AddRange(cmdParameters);
conn.Open();
return cmd.ExecuteReader(CommandBehavior.Default);
}
}
}
DataReader
实际上有一个针对这种情况的行为,CommandBehavior.CloseConnection
。
public SqlDataReader ExecuteDataReaderAsync(string connectionString, CommandType cmdType, string spName, params SqlParameter[] cmdParameters)
{
// These two are intentionally are not in a using statement, but it is ok, closing
// the reader cleans up the resources.
var conn = new SqlConnection(connectionString))
var cmd = new SqlCommand(spName, conn))
cmd.CommandType = cmdType;
cmd.Parameters.AddRange(cmdParameters);
conn.Open();
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
当您处置DataReader
时,它将关闭SqlConnection
,这是处置连接无论如何都会做的(加上引发Disposed
事件)。
处理SqlCommand
所做的唯一事情是释放对内部变量_cachedMetaData
的引用,以允许它提前被GC,并在Component
上调用基础Dispose(bool)
,它所做的唯一事情是引发Disposed
事件。
只要您不使用连接或命令或连接的Disposed
事件,此解决方案应该适用于您。
如果您确实必须"正确"处理这两个,因为您确实依赖于事件,请使用类似于我在处理CryptoStream
时遇到的类似情况时所做的技巧。创建一个包装器,该包装器将在处置读取器时处置连接和命令。
sealed class CleaningDataReader : IDataReader
{
private readonly IDataReader _reader;
private readonly IDisposable[] _itemsToDispose;
public CleaningDataReader(IDataReader reader, params IDisposable[] itemsToDispose)
{
if(reader == null)
throw new ArgumentNullException("reader");
_reader = reader;
_itemsToDispose = itemsToDispose;
}
public void Dispose()
{
_reader.Dispose();
if (_itemsToDispose != null)
{
foreach (var item in _itemsToDispose)
{
if(item != null)
item.Dispose();
}
}
}
public void Close()
{
_reader.Close();
}
public int Depth
{
get { return _reader.Depth; }
}
public int FieldCount
{
get { return _reader.FieldCount; }
}
public bool GetBoolean(int i)
{
return _reader.GetBoolean(i);
}
public byte GetByte(int i)
{
return _reader.GetByte(i);
}
public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
{
return _reader.GetBytes(i, fieldOffset, buffer, bufferoffset, length);
}
public char GetChar(int i)
{
return _reader.GetChar(i);
}
public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
{
return _reader.GetChars(i, fieldoffset, buffer, bufferoffset, length);
}
public IDataReader GetData(int i)
{
return _reader.GetData(i);
}
public string GetDataTypeName(int i)
{
return _reader.GetDataTypeName(i);
}
public DateTime GetDateTime(int i)
{
return _reader.GetDateTime(i);
}
public decimal GetDecimal(int i)
{
return _reader.GetDecimal(i);
}
public double GetDouble(int i)
{
return _reader.GetDouble(i);
}
public Type GetFieldType(int i)
{
return _reader.GetFieldType(i);
}
public float GetFloat(int i)
{
return _reader.GetFloat(i);
}
public Guid GetGuid(int i)
{
return _reader.GetGuid(i);
}
public short GetInt16(int i)
{
return _reader.GetInt16(i);
}
public int GetInt32(int i)
{
return _reader.GetInt32(i);
}
public long GetInt64(int i)
{
return _reader.GetInt64(i);
}
public string GetName(int i)
{
return _reader.GetName(i);
}
public int GetOrdinal(string name)
{
return _reader.GetOrdinal(name);
}
public DataTable GetSchemaTable()
{
return _reader.GetSchemaTable();
}
public string GetString(int i)
{
return _reader.GetString(i);
}
public object GetValue(int i)
{
return _reader.GetValue(i);
}
public int GetValues(object[] values)
{
return _reader.GetValues(values);
}
public bool IsClosed
{
get { return _reader.IsClosed; }
}
public bool IsDBNull(int i)
{
return _reader.IsDBNull(i);
}
public object this[int i]
{
get { return _reader[i]; }
}
public object this[string name]
{
get { return _reader[name]; }
}
public bool NextResult()
{
return _reader.NextResult();
}
public bool Read()
{
return _reader.Read();
}
public int RecordsAffected
{
get { return _reader.RecordsAffected; }
}
}
使用public IDataReader ExecuteDataReaderAsync(string connectionString, CommandType cmdType, string spName, params SqlParameter[] cmdParameters)
{
// These two are intentionally are not in a using statement, but it is ok, closing
// the reader cleans up the resources.
var conn = new SqlConnection(connectionString))
var cmd = new SqlCommand(spName, conn))
cmd.CommandType = cmdType;
cmd.Parameters.AddRange(cmdParameters);
conn.Open();
var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
return new CleaningDataReader(reader, cmd, conn);
}
我仍然会使用第一种方法,除非你真的需要触发dispose事件。
编辑:我同意Scott Chamberlain的解决方案。看起来不错。
您需要将SqlConnection/SqlCommand初始化移出ExecuteDataReaderAsync
方法(使包装器变得毫无意义)或逐步遍历所有数据,存储值。
这是一种方法:
public List<Dictionary<string, object>> ExecuteDataReaderAsync(string connectionString, CommandType cmdType, string spName, params SqlParameter[] cmdParameters) {
//TODO make async once fixed problem
var records = new List<Dictionary<string, object>>();
using (var conn = new SqlConnection(connectionString)) {
conn.Open();
using (var cmd = new SqlCommand(spName, conn)) {
cmd.CommandType = cmdType;
cmd.Parameters.AddRange(cmdParameters);
using (var rdr = cmd.ExecuteReader(CommandBehavior.Default)) {
while (rdr.Read()) {
var record = new Dictionary<string, object>();
for (int fieldIndex = 0; fieldIndex < rdr.FieldCount; fieldIndex++) {
record.Add(rdr.GetName(fieldIndex), rdr.GetValue(fieldIndex));
}
records.Add(record);
}
}
}
}
return results;
}
不幸的是,我现在不能测试这个。但它应该是接近正确的