public async Task<StreamEventsPage> ReadStream(string streamId, int fromVersion, int count, StreamReadDirection direction = StreamReadDirection.Forward) { Guard.NullOrWhiteSpace(() => streamId); await _log.Debug("Reading stream {@streamId} from version {fromVersion} to version {count}", streamId, fromVersion, count); // create parameters var parameters = new DynamicParameters(); parameters.AddDynamicParams(new { StreamId = streamId, FromVersion = fromVersion, Count = count, ReadForward = direction == StreamReadDirection.Forward ? 1 : 0 }); parameters.AddOutput("Error"); parameters.AddReturnValue(); IEnumerable<StreamEventData> result; // execute operation using(var connection = new SqlConnection(_settings.ConnectionString)) { // found a hardcore bug with Dapper! // if we exit a query without executing a select, the .QueryAsync<T> fails because it // tries to read the resultsetschema and fails. // therefore we do not have access to return or output values. result = await connection .QueryAsync<StreamEventData>( sql : "ReadStream", param : parameters, commandType: CommandType.StoredProcedure) .ConfigureAwait(false); } // check for errors switch(parameters.GetOutput<int>("Error")) { case -100: throw new StreamNotFoundException(streamId); case -200: throw new StreamDeletedException(streamId, fromVersion); } await _log.Information("Stream {@streamId} read from version {fromVersion} to version {count}", streamId, fromVersion, count); // return stream page var streamVersion = parameters.GetReturnValue(); var streamEvents = result.Select( streamEventData => { var metadata = _serializer.Deserialize<IDictionary<string, string>>(streamEventData.Metadata); var domainEventType = Type.GetType(metadata[EventMetadataKeys.ClrType].ToString()); var streamEvent = _serializer.DeserializeAs<object>(streamEventData.Data, domainEventType); return new StreamEvent(streamEvent, metadata); }).ToList(); var lastReadEventVersion = streamEvents.Any() ? int.Parse(streamEvents.Last().Metadata[EventMetadataKeys.Version]) : -1; await _log.Debug("Stream {@streamId} event serialization finished", streamId); return new StreamEventsPage( streamId : streamId, fromVersion: fromVersion, toVersion : lastReadEventVersion, lastVersion: streamVersion, events : streamEvents, direction : direction); }