private async Task AppendToStreamExpectedVersionAny(
            StreamIdInfo streamIdInfo,
            int expectedVersion,
            NewStreamEvent[] events,
            CancellationToken cancellationToken)
        {
            var sqlDataRecords = CreateSqlDataRecords(events);

            using (var connection = _createConnection())
            {
                await connection.OpenAsync(cancellationToken).NotOnCapturedContext();

                using (var command = new SqlCommand(_scripts.AppendStreamExpectedVersionAny, connection))
                {
                    command.Parameters.AddWithValue("streamId", streamIdInfo.Hash);
                    command.Parameters.AddWithValue("streamIdOriginal", streamIdInfo.Id);
                    var eventsParam = CreateNewEventsSqlParameter(sqlDataRecords);
                    command.Parameters.Add(eventsParam);

                    try
                    {
                        await command
                        .ExecuteNonQueryAsync(cancellationToken)
                        .NotOnCapturedContext();
                    }
                    catch (SqlException ex)
                    {
                        // Check for unique constraint violation on
                        // https://technet.microsoft.com/en-us/library/aa258747%28v=sql.80%29.aspx
                        if (ex.IsUniqueConstraintViolationOnIndex("IX_Events_StreamIdInternal_Id"))
                        {
                            // Idempotency handling. Check if the events have already been written.

                            var page = await ReadStreamInternal(
                                streamIdInfo.Id,
                                StreamVersion.Start,
                                events.Length,
                                ReadDirection.Forward,
                                connection,
                                cancellationToken)
                                       .NotOnCapturedContext();

                            if (events.Length > page.Events.Length)
                            {
                                throw new WrongExpectedVersionException(
                                          Messages.AppendFailedWrongExpectedVersion(streamIdInfo.Id, expectedVersion),
                                          ex);
                            }

                            for (int i = 0; i < Math.Min(events.Length, page.Events.Length); i++)
                            {
                                if (events[i].EventId != page.Events[i].EventId)
                                {
                                    throw new WrongExpectedVersionException(
                                              Messages.AppendFailedWrongExpectedVersion(streamIdInfo.Id, expectedVersion),
                                              ex);
                                }
                            }

                            return;
                        }

                        if (ex.IsUniqueConstraintViolation())
                        {
                            throw new WrongExpectedVersionException(
                                      Messages.AppendFailedWrongExpectedVersion(streamIdInfo.Id, expectedVersion),
                                      ex);
                        }

                        throw;
                    }
                }
            }
        }
        private async Task <StreamEventsPage> ReadStreamInternal(
            string streamId,
            int start,
            int count,
            ReadDirection direction,
            SqlConnection connection,
            CancellationToken cancellationToken)
        {
            var streamIdInfo = new StreamIdInfo(streamId);

            var streamVersion = start == StreamVersion.End ? int.MaxValue : start;
            // To read backwards from end, need to use int MaxValue
            string commandText;
            Func <List <StreamEvent>, int> getNextSequenceNumber;

            if (direction == ReadDirection.Forward)
            {
                commandText           = _scripts.ReadStreamForward;
                getNextSequenceNumber = events => events.Last().StreamVersion + 1;
            }
            else
            {
                commandText           = _scripts.ReadStreamBackward;
                getNextSequenceNumber = events => events.Last().StreamVersion - 1;
            }

            using (var command = new SqlCommand(commandText, connection))
            {
                command.Parameters.AddWithValue("streamId", streamIdInfo.Hash);
                command.Parameters.AddWithValue("count", count + 1); //Read extra row to see if at end or not
                command.Parameters.AddWithValue("StreamVersion", streamVersion);

                var streamEvents = new List <StreamEvent>();

                var reader = await command.ExecuteReaderAsync(cancellationToken).NotOnCapturedContext();

                await reader.ReadAsync(cancellationToken).NotOnCapturedContext();

                var doesNotExist = reader.IsDBNull(0);
                if (doesNotExist)
                {
                    return(new StreamEventsPage(
                               streamId,
                               PageReadStatus.StreamNotFound,
                               start,
                               -1,
                               -1,
                               direction,
                               isEndOfStream: true));
                }

                // Read IsDeleted result set
                var isDeleted = reader.GetBoolean(0);
                if (isDeleted)
                {
                    return(new StreamEventsPage(
                               streamId,
                               PageReadStatus.StreamDeleted,
                               0,
                               0,
                               0,
                               direction,
                               isEndOfStream: true));
                }


                // Read Events result set
                await reader.NextResultAsync(cancellationToken).NotOnCapturedContext();

                while (await reader.ReadAsync(cancellationToken).NotOnCapturedContext())
                {
                    var streamVersion1 = reader.GetInt32(0);
                    var ordinal        = reader.GetInt64(1);
                    var eventId        = reader.GetGuid(2);
                    var created        = reader.GetDateTime(3);
                    var type           = reader.GetString(4);
                    var jsonData       = reader.GetString(5);
                    var jsonMetadata   = reader.GetString(6);

                    var streamEvent = new StreamEvent(
                        streamId,
                        eventId,
                        streamVersion1,
                        ordinal,
                        created,
                        type,
                        jsonData,
                        jsonMetadata);

                    streamEvents.Add(streamEvent);
                }

                // Read last event revision result set
                await reader.NextResultAsync(cancellationToken).NotOnCapturedContext();

                await reader.ReadAsync(cancellationToken).NotOnCapturedContext();

                var lastStreamVersion = reader.GetInt32(0);

                var isEnd = true;
                if (streamEvents.Count == count + 1)
                {
                    isEnd = false;
                    streamEvents.RemoveAt(count);
                }

                return(new StreamEventsPage(
                           streamId,
                           PageReadStatus.Success,
                           start,
                           getNextSequenceNumber(streamEvents),
                           lastStreamVersion,
                           direction,
                           isEnd,
                           streamEvents.ToArray()));
            }
        }
        private async Task AppendToStreamExpectedVersion(
            string streamId,
            int expectedVersion,
            NewStreamEvent[] events,
            StreamIdInfo streamIdHash,
            CancellationToken cancellationToken)
        {
            var sqlDataRecords = CreateSqlDataRecords(events);

            using (var connection = _createConnection())
            {
                await connection.OpenAsync(cancellationToken).NotOnCapturedContext();

                using (var command = new SqlCommand(_scripts.AppendStreamExpectedVersion, connection))
                {
                    command.Parameters.AddWithValue("streamId", streamIdHash.Hash);
                    command.Parameters.AddWithValue("expectedStreamVersion", expectedVersion);
                    var eventsParam = CreateNewEventsSqlParameter(sqlDataRecords);
                    command.Parameters.Add(eventsParam);

                    try
                    {
                        await command
                        .ExecuteNonQueryAsync(cancellationToken)
                        .NotOnCapturedContext();
                    }
                    catch (SqlException ex)
                    {
                        if (ex.Errors.Count == 1)
                        {
                            var sqlError = ex.Errors[0];
                            if (sqlError.Message == "WrongExpectedVersion")
                            {
                                // Idempotency handling. Check if the events have already been written.

                                var page = await ReadStreamInternal(streamId,
                                                                    expectedVersion + 1, // when reading for already written events, it's from the one after the expected
                                                                    events.Length,
                                                                    ReadDirection.Forward,
                                                                    connection,
                                                                    cancellationToken);

                                if (events.Length > page.Events.Length)
                                {
                                    throw new WrongExpectedVersionException(
                                              Messages.AppendFailedWrongExpectedVersion(streamId, expectedVersion),
                                              ex);
                                }

                                for (int i = 0; i < Math.Min(events.Length, page.Events.Length); i++)
                                {
                                    if (events[i].EventId != page.Events[i].EventId)
                                    {
                                        throw new WrongExpectedVersionException(
                                                  Messages.AppendFailedWrongExpectedVersion(streamId, expectedVersion),
                                                  ex);
                                    }
                                }

                                return;
                            }
                        }
                        if (ex.IsUniqueConstraintViolation())
                        {
                            throw new WrongExpectedVersionException(
                                      Messages.AppendFailedWrongExpectedVersion(streamId, expectedVersion),
                                      ex);
                        }
                        throw;
                    }
                }
            }
        }