2016-11-24 20 views
5

Otrzymuję nieparzysty problem, w którym mogę zwrócić wyniki z wywołania procedury składowanej, ale kod retrospektywnie kończy się niepowodzeniem.Właściwość połączenia SqlDataReader ma wartość 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); 
     } 
    } 
} 

Wywołanie tego kodu pokazuje, że zmienna x zawsze reprezentuje liczbę wierszy bym spodziewać się od wezwania do moich procedur przechowywanych.

Dodatkowo moje dane wyjściowe debugowania pokazują wartości identyfikacyjne, które spodziewam się zobaczyć.

Jednak po zwróceniu wyników otrzymuję komunikat o błędzie An exception of type 'System.InvalidOperationException' occurred in System.Data.dll but was not handled in user code z wiersza if (reader.HasRows) (tj. Który został już wykonany). Przeglądarka, z której uruchamiam to żądanie, pokazuje HTTP Error 502.3 - Bad Gateway.

Screenshot of Error

Screenshot of HasRows Behaviour

Podejrzewam, że powodem jest System na obliczaniu wartości dla debugowania ID i X oddzielnie, jak to powrót prawdziwego wyjścia użytkownika. W związku z tym wykonuje on leniwe operacje, aby uzyskać wartości IEnumerable w punkcie, w którym musi je zwrócić; tylko w tym punkcie instrukcje using spowodowały wywołanie metod utylizacji, a zatem połączenie czytnika jest zerowe (to widzę, gdy sprawdzam właściwości zmiennej reader podczas debugowania).

Czy ktoś widział takie zachowanie przed/jest to błąd; lub czy właśnie przeoczyłem coś oczywistego?


dodatkowe Kod:

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

masz starał się kontrolować (lub użyć w kodzie) '' reader.HasRows' tuż po czytniku SqlDataReader = cmd.ExecuteReader() ; 'i przed wywołaniem' MapRecordsToDTOs'? – McNets

+1

możesz spróbować: 'reader = czekaj cmd.ExecuteReaderAsync();' – McNets

+0

@mcNets: Funkcja 'MapRecordsToDTOs' potwierdza, że' reader.HasRows' jest prawdą, ponieważ jest w stanie iterować przez te wiersze, aby umożliwić 'x' uzyskanie poprawnej wartości, a' '' '' '' '' '' '' '' ', aby poprawnie wyprowadzić' Debug.WriteLine'. sprawozdania. – JohnLBevan

Odpowiedz

2

ja tymczasowo wdrożone obejście, które rozwiązuje ten problem.

to działa:

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(); 
} 

W przeciwieństwie do oryginału:

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

Różnica jest przenieść kod dotknięte przez iterator taki sposób, że tylko iteracji wyników na liście; i nie polega na kompilatorze, który rozsądnie rozumie wymagania związane z obiektami IDisposable.

Rozumiem, że kompilator powinien być w stanie obsłużyć to dla mnie (potwierdzone tutaj: https://stackoverflow.com/a/13504789/361842), więc podejrzewam, że jest to błąd w kompilatorze.

Zgłaszane tutaj: https://connect.microsoft.com/VisualStudio/feedback/details/3113138

Dodatkowy kod demo tutaj: https://gist.github.com/JohnLBevan/a910d886df577e442e2f5a9c2dd41293/

Powiązane problemy