使用ado.net包装类关闭阅读器时无效尝试调用HasRows

时间:2015-09-04 13:31:32

标签: c# ado.net

我正在尝试为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);
                }
            }
        }

2 个答案:

答案 0 :(得分:1)

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并在{{{{}}上调用基础Dispose(bool) 1}}它唯一能做的就是引发Component事件。

只要您没有使用连接的Disposed事件或命令或连接,此解决方案就适合您。

如果你真的必须&#34;正确&#34;处置这两个因为你依赖于事件,使用类似于I ran in to a similar situation处理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; }
    }
}

我仍然会采用第一种方法,除非你真的需要解雇Disposed事件。

答案 1 :(得分:0)

编辑:我会选择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;
    }

不幸的是,我目前无法对此进行测试。但它应该接近正确。