2016-11-24 4 views
5

Я получаю странную проблему, когда я могу вернуть результаты вызова из хранимой процедуры, но код ретроспективно терпит неудачу.Свойство SqlDataReader's Connection Null

public IEnumerable<T> ExecuteStoredProcedure<T>(string storedProcedureName, IDataMapper<T> mapper, IDictionary<string, object> parameters) 
{ 
    using (var connection = new SqlConnection(connectionString)) 
    { 
     using (var cmd = new SqlCommand(storedProcedureName, connection)) 
     { 
      cmd.CommandType = CommandType.StoredProcedure; 
      foreach (var key in parameters.Keys) 
      { 
       cmd.Parameters.AddWithValue(key, parameters[key]); 
      } 
      connection.Open(); 
      SqlDataReader reader = cmd.ExecuteReader(); 
      //return MapRecordsToDTOs(reader, mapper); 

      //let's test: 
      IEnumerable<T> result = MapRecordsToDTOs(reader, mapper); 
      var x = (new List<T>(result)).Count; 
      System.Diagnostics.Debug.WriteLine(x); 
      return result; 
     } 
    } 
} 


private static IEnumerable<T> MapRecordsToDTOs<T>(SqlDataReader reader, IDataMapper<T> mapper) 
{ 
    if (reader.HasRows) 
    { 
     while (reader.Read()) 
     { 
      System.Diagnostics.Debug.WriteLine(reader["Id"]); //what's going on... 
      yield return mapper.MapToDto((IDataRecord)reader); 
     } 
    } 
} 

Вызов этого кода показывает, что переменная x всегда представляет количество строк, я бы ожидать от вызова моих хранимых процедур.

Кроме того, мой вывод отладки показывает идентификационные значения, которые я ожидаю увидеть.

Однако после получения этих результатов я получаю сообщение об ошибке An exception of type 'System.InvalidOperationException' occurred in System.Data.dll but was not handled in user code из строки if (reader.HasRows) (т. Е. Уже выполненной). Браузер, из которого я вызываю этот запрос, показывает HTTP Error 502.3 - Bad Gateway.

Screenshot of Error

Screenshot of HasRows Behaviour

Я подозреваю, что причина заключается в системы расчета ID и X значения для отладки отдельно, как это будет вернуть реальный выход пользователя. Таким образом, он выполняет ленивую операцию для получения значений IEnumerable в момент, когда он должен их возвращать; только к этой точке операторы using вызвали вызовы методов dispose, и поэтому соединение читателя является нулевым (это то, что я вижу, когда проверяю свойства переменной reader во время отладки).

Кто-нибудь видел подобное поведение раньше/это ошибка; или я просто пропустил что-то очевидное?


Дополнительный код:

public interface IDataMapper<T> 
{ 
    T MapToDto(IDataRecord record); 
} 

public class CurrencyMapper: IDataMapper<CurrencyDTO> 
{ 
    const string FieldNameCode = "Code"; 
    const string FieldNameId = "Id"; 
    const string FieldNameName = "Name"; 
    const string FieldNameNum = "Num"; 
    const string FieldNameE = "E"; 
    const string FieldNameSymbol = "Symbol"; 

    public CurrencyMapper() { } 

    public CurrencyDTO MapToDto(IDataRecord record) 
    { 
     var code = record[FieldNameCode] as string; 
     var id = record[FieldNameId] as Guid?; 
     var name = record[FieldNameName] as string; 
     var num = record[FieldNameNum] as string; 
     var e = record[FieldNameE] as int?; 
     var symbol = record[FieldNameSymbol] as char?; 
     return new CurrencyDTO(id, code, num, e, name, symbol); 
    } 
} 

public class CurrencyRepository 
{ 

    const string SPReadAll = "usp_CRUD_Currency_ReadAll"; 

    readonly SqlDatabase db; 
    public CurrencyRepository() 
    { 
     db = new SqlDatabase(); //stick to SQL only for the moment for simplicity 
    } 
    public IEnumerable<CurrencyDTO> GetCurrencyCodes() 
    { 
     var mapper = new CurrencyMapper(); 
     return db.ExecuteStoredProcedure(SPReadAll, mapper); 
    } 
} 

public class CurrencyDTO 
{ 

    readonly Guid? id; 
    readonly string code; 
    readonly string num; 
    readonly int? e; 
    readonly string name; 
    readonly char? symbol; 

    public CurrencyDTO(Guid? id,string code,string num,int? e,string name, char? symbol) 
    { 
     this.id = id; 
     this.code = code; 
     this.num = num; 
     this.e = e; 
     this.name = name; 
     this.symbol = symbol; 
    } 

    public Guid? Id { get { return id; } } 
    public string Code { get { return code; } } 
    public string Num { get { return num; } } 
    public int? E { get { return e; } } 
    public string Name { get { return name; } } 
    public char? Symbol { get { return symbol; } } 
} 
+1

вы пытались осмотреть (или использовать в своем коде) 'reader.HasRows' только после того, как' SqlDataReader читателя = cmd.ExecuteReader() ; 'и раньше, чтобы вызвать« MapRecordsToDTOs »? – McNets

+1

вы можете попробовать: 'reader = await cmd.ExecuteReaderAsync();' – McNets

+0

@mcNets: Функция 'MapRecordsToDTO' подтверждает это' читателя.HasRows' истинно, так как он может перебирать эти строки, чтобы позволить 'x' получить правильное значение, а для' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' 'правильно' '. заявления. – JohnLBevan

ответ

2

Я временно реализован обходной путь, который решает эту проблему.

Это работает:

private static IEnumerable<T> MapRecordsToDTOs<T>(SqlDataReader reader, IDataMapper<T> mapper) 
{ 
    var list = new List<T>(); //use a list to force eager evaluation 
    if (reader.HasRows) 
    { 
     while (reader.Read()) 
     { 
      list.Add(mapper.MapToDto((IDataRecord)reader)); 
     } 
    } 
    return list.ToArray(); 
} 

В отличие от оригинала:

private static IEnumerable<T> MapRecordsToDTOs<T>(SqlDataReader reader, IDataMapper<T> mapper) 
{ 
    if (reader.HasRows) 
    { 
     while (reader.Read()) 
     { 
      yield return mapper.MapToDto((IDataRecord)reader); 
     } 
    } 
} 

Разница в том, я переместить код, пострадавших от итератора, так что он только перебирает результатов в списке; и не полагается на компилятор, разумно понимая требования, связанные с объектами IDisposable.

Я понимаю, что компилятор должен иметь возможность справиться с этим для меня (подтверждено здесь: https://stackoverflow.com/a/13504789/361842), поэтому я подозреваю, что это ошибка в компиляторе.

Сообщил здесь: https://connect.microsoft.com/VisualStudio/feedback/details/3113138

Дополнительная демо-код здесь: https://gist.github.com/JohnLBevan/a910d886df577e442e2f5a9c2dd41293/

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