2015-09-04 6 views
0

Я пытаюсь написать класс-оболочку для ado.net, проблема в том, что я получаю следующую ошибку, и я не знаю, как ее решить.Неверная попытка вызвать HasRows, когда читатель закрыт с помощью класса ado.net wrapper

Я прочитал много других вопросов об этом, но никто не использует класс-оболочку и, как таковой, не помогает.

Может ли кто-нибудь указать мне в правильном направлении или предоставить ссылку с той же проблемой, чтобы я мог прочитать, как ее решить. Или я полностью ошибаюсь.

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); 
       } 
      } 
     } 
+0

'rdr' зависит от SqlCommand (который зависит от SqlConnection). Но оба этих объекта удаляются перед тем, как выйти из метода ExecuteDataReaderAsync. – Ulric

+0

@Ulric Как я могу решить проблему –

+0

См. Ответ ниже :) – Ulric

ответ

0

EDIT: Я бы с решением Скотта Чемберлена. Выглядит неплохо.

Вам необходимо либо переместить инициализацию 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; 
    } 

К сожалению, я не могу проверить это на данный момент. Но он должен быть близок к правильности.

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'ed раньше и вызвать базу Dispose(bool) на Component, который только что он делает это поднять Disposed событие.

До тех пор, пока вы не используете событие Disposed, соединение или команду или соединение это решение должно работать на вас.


Если вы действительно должны «правильно» распоряжаться этими двумя, потому что вы полагаться на события, использовать трюк, похожий на то, что я сделал, когда I ran in to a similar situation в расположени 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); 
} 

Я бы еще пойти с первым подходом, если вы действительно не нужна Disposed событие увольняют.

Смежные вопросы