public async Task <StreamReadResults> ReadFrom(string streamId, Func <IAmAStoredEvent, bool> predicate = null, StreamReadDirection direction = StreamReadDirection.Backwards, CancellationToken cancellationToken = default)
        {
            var readResult = client.ReadStreamAsync(
                direction == StreamReadDirection.Backwards ? Direction.Backwards : Direction.Forwards,
                streamId,
                direction == StreamReadDirection.Backwards ? StreamPosition.End : StreamPosition.Start,
                resolveLinkTos: true,
                configureOperationOptions: options => options.TimeoutAfter = TimeSpan.FromSeconds(30),
                cancellationToken: cancellationToken);

            bool streamExists = false;

            try
            {
                var readState = await readResult.ReadState;
                streamExists = readState == ReadState.Ok;
            }
#pragma warning disable 168
            catch (StreamDeletedException ex)
            // This happens when the stream is hard-deleted. We don't want to throw in that case
#pragma warning restore 168
            {
                streamExists = false;
            }

            if (!streamExists)
            {
                return(new StreamReadResults(EmptyReadResult, false, StoredEventPosition.FromInt64(-1)));
            }

            predicate ??= _ => true;

            var lastIndex = (await client.ReadStreamAsync(Direction.Backwards, streamId,
                                                          StreamPosition.End,
                                                          1,
                                                          resolveLinkTos: false).FirstAsync(cancellationToken)).OriginalEventNumber;

            IAsyncEnumerable <StoredEvent> storedEvents;
            if (direction == StreamReadDirection.Backwards)
            {
                storedEvents = readResult
                               // Trust me, resharper is wrong in this one. Event can be null
                               // ReSharper disable once ConditionIsAlwaysTrueOrFalse
                               .Where(e => e.Event != null)
                               .Select(e => e.Event.ToStoredEvent(stateFactory))
                               .TakeWhile(e => e.DeserializedEvent is not EntitySoftDeleted)
                               .Where(e => predicate(e));
            }
            else
            {
                storedEvents = readResult
                               // Trust me, resharper is wrong in this one. Event can be null
                               // ReSharper disable once ConditionIsAlwaysTrueOrFalse
                               .Where(e => e.Event != null)
                               .Select(e => e.Event.ToStoredEvent(stateFactory))
                               .Where(e => predicate(e));
            }

            return(new StreamReadResults(storedEvents, true, StoredEventPosition.FromInt64(lastIndex.ToInt64())));
        }
        public async Task <StreamReadResults> ReadFrom(string streamId, Func <IAmAStoredEvent, bool> predicate = null, StreamReadDirection direction = StreamReadDirection.Backwards, CancellationToken cancellationToken = default)
        {
            predicate ??= _ => true;

            IAsyncEnumerable <StoredEvent> storedEvents;

            if (direction == StreamReadDirection.Backwards)
            {
                var readResult = await connection.ReadStreamEventsBackwardAsync(streamId, StreamPosition.End, pageSize, true);

                if (!StreamExists(readResult))
                {
                    return(new StreamReadResults(EmptyReadResult, false, StoredEventPosition.FromInt64(-1)));
                }

                storedEvents = readResult.Events
                               // Trust me, resharper is wrong in this one. Event can be null
                               // ReSharper disable once ConditionIsAlwaysTrueOrFalse
                               .Where(e => e.Event != null)
                               .Select((e, _) => e.Event.ToStoredEvent(stateFactory))
                               .TakeWhile(e => e.DeserializedEvent is not EntitySoftDeleted)
                               .Where(e => predicate(e))
                               .ToAsyncEnumerable();
            }
            else
            {
                var readResult = await connection.ReadStreamEventsForwardAsync(streamId, StreamPosition.Start, pageSize, true);

                if (!StreamExists(readResult))
                {
                    return(new StreamReadResults(EmptyReadResult, false, StoredEventPosition.FromInt64(-1)));
                }

                storedEvents = readResult.Events
                               // Trust me, resharper is wrong in this one. Event can be null
                               // ReSharper disable once ConditionIsAlwaysTrueOrFalse
                               .Where(e => e.Event != null)
                               .Select((e, c) => e.Event.ToStoredEvent(stateFactory))
                               .Where(e => predicate(e))
                               .ToAsyncEnumerable();
            }

            var result = await connection.ReadEventAsync(streamId, StreamPosition.End, false);

            var idx = result.Event?.OriginalEventNumber ?? -1;

            return(new StreamReadResults(storedEvents, true, StoredEventPosition.FromInt64(idx)));
        }
 public StreamReadResults(IAsyncEnumerable <StoredEvent> events, bool streamExists, StoredEventPosition storedEventPosition)
 {
     Events              = events ?? throw new ArgumentNullException(nameof(events));
     StreamExists        = streamExists;
     StoredEventPosition = storedEventPosition;
 }