public bool Read() { ThrowIfDisposed(); var hasData = _reader.Read(_readerId); if (hasData) { // If there is a row to read data for then get all of the values for it and, if we don't already have them, the names // of the fields in the current result set. This will allow any requests for individual fields in the row (whether // requested by field name or by index) to be returned from data in memory, rather than having to go back to the // remote host every time (chatty WCF services can be slow). This has the disadvantage that every value from // current row has been pulled in to memory and we might not need them all, but hopefully that's worth the // trade-off (and, where performance is important, queries should only specify fields they want data for). if (_currentColumnNamesLookupIfKnown == null) { var fieldNames = _reader.GetFieldNames(_readerId); _currentColumnNamesLookupIfKnown = new Dictionary <string, int>(StringComparer.OrdinalIgnoreCase); for (var i = 0; i < fieldNames.Length; i++) { if (!_currentColumnNamesLookupIfKnown.ContainsKey(fieldNames[i])) { _currentColumnNamesLookupIfKnown.Add(fieldNames[i], i); } } } // If any field names are repeated in the query (which can happen if the the same name is explicitly listed multiple times // or there is a SELECT * and tables are joined that have columns with the same name) then the number of entries in the // _currentColumnNamesLookupIfKnown dictionary will be fewer than the actual number of fields in the recordset. So, // pass int.MaxValue as the maximumNumberOfValuesToRead argument in the GetValues call in order to get as many // values are there are fields. _valuesInCurrentRowIfKnown = _reader.GetValues(_readerId, int.MaxValue); } else { _valuesInCurrentRowIfKnown = null; // If there is no more data then there can be no values for the current row } return(hasData); }