public async Task <PagedCollection <Models.Requests.Request> > GetRequestsByDateRangeAsync(RouteIdentifier id, int skip, int take, DateTimeOffset?from, DateTimeOffset?to)
        {
            using (var db = new SubrouteContext())
            {
                // Limit the request results by date range if one was provided.
                var query = db.Requests.AsNoTracking()
                            .Where(r => from.HasValue && r.OccurredOn >= from)
                            .Where(r => to.HasValue && r.OccurredOn <= to);

                // Ensure we are only returning requests for the specified route.
                query = id.Type == RouteIdentifierType.Id
                    ? query.Where(r => r.RouteId == id.Id)
                    : query.Where(r => r.Route.Uri == id.Uri);

                // Apply an order to the query to reverse chronological.
                query = query.OrderByDescending(r => r.OccurredOn);

                // Project and page query results into our wire format and materialize.
                // We project the results so we don't pull back the entire payload contents.
                var pagedResults = await query
                                   .Skip(skip)
                                   .Take(take)
                                   .Select(Models.Requests.Request.Map)
                                   .ToArrayAsync();

                // Get total count and final projected results.
                var total = await query.CountAsync();

                // Iterate the results and deserialize the headers for each request.
                foreach (var request in pagedResults)
                {
                    request.RequestHeaders  = HeaderUtility.DeserializeHeaders(request.SerializedRequestHeaders);
                    request.ResponseHeaders = HeaderUtility.DeserializeHeaders(request.SerializedResponseHeaders);
                }

                return(new PagedCollection <Models.Requests.Request>
                {
                    Skip = skip,
                    Take = take,
                    TotalCount = total,
                    Results = pagedResults
                });
            }
        }
        public async Task <ExecutionResponse> ExecuteRouteAsync(string route, ExecutionRequest request)
        {
            // Load the route to be executed so we can get its identifier.
            var routeEntry = await _traceContext.TraceTimeAsync("Load-Route", _routeRepository.GetRouteByIdentifierAsync(route));

            // Ensure the route is online.
            if (!routeEntry.IsOnline)
            {
                throw new OfflineException("Route is offline.");
            }

            // Ensure the route is published.
            if (!routeEntry.PublishedOn.HasValue)
            {
                throw new OfflineException("Route has not yet been published.");
            }

            // Create request data in database which we'll match the response to after execution.
            var serializedHeaders = HeaderUtility.SerializeHeaders(request.Headers);
            var requestEntry      = new Request
            {
                RouteId        = routeEntry.Id,
                Method         = request.Method.ToString(),
                Uri            = request.Uri.ToString(),
                RequestHeaders = serializedHeaders,
                RequestPayload = request.Body,
                IpAddress      = request.IpAddress,
                OccurredOn     = DateTimeOffset.UtcNow
            };

            requestEntry = await _traceContext.TraceTimeAsync("Create-Request", _requestRepository.CreateRequestAsync(requestEntry));

            // We will send just a pointer to the request in the database via the service bus to side step size restrictions.
            // We'll use the machine name as the correlation ID to allow the message to be returned to this waiting instance.
            var requestMessage = new BrokeredMessage
            {
                ReplyTo       = Settings.ServiceBusResponseTopicName,
                CorrelationId = Environment.MachineName
            };

            requestMessage.Properties["RouteId"]     = routeEntry.Id;
            requestMessage.Properties["RequestId"]   = requestEntry.Id;
            requestMessage.Properties["MachineName"] = Environment.MachineName;

            var topicClient = await _traceContext.TraceTimeAsync("Create-Topic-Client", _topicFactory.CreateTopicClientAsync(Settings.ServiceBusRequestTopicName));

            await _traceContext.TraceTimeAsync("Send-Request", topicClient.SendAsync(requestMessage));

            // We'll try to get a message every second to find our desired message. Otherwise we'll keep trying for up to configured duration.
            var maxWaitTime = TimeSpan.FromMinutes(Settings.ServiceBusResponseTimeoutMinutes);

            try
            {
                // We'll listen on the response pipeline observable for incoming messages that originated as
                // requests from this machine, and are filtered by CorrelationId (which is the machine name).
                // We have added a filter that will only listen for messages generated from this waiting
                // thread. We'll also specify that we'll wait no longer than the maximum specified timeout.
                // When we receive the message, we'll load the details from the database and convert it to
                // an ExecutionResponse object and select the first one (which there should only be one).
                var executionResponse = await _traceContext.TraceTimeAsync("Receive-Generate-Response", _responsePipeline.ResponseMessages
                                                                           .Where(bm => (int)bm.Properties["RequestId"] == requestEntry.Id)
                                                                           .Timeout(maxWaitTime)
                                                                           .Select(async bm =>
                {
                    // We have found our message, requery the database to get the response data.
                    var responseEntry = await _traceContext.TraceTimeAsync("Load-Request-By-ID", _requestRepository.GetRequestByIdAsync(requestEntry.Id));

                    // Build a ExecutionResponse using details from database.
                    var responseStatusCode = (HttpStatusCode)(responseEntry.StatusCode ?? 204);
                    var deserializedHeaders = _traceContext.TraceTime("Deserialize-Headers", () => ExecutionResponse.DeserializeHeaders(responseEntry.ResponseHeaders));
                    var response = new ExecutionResponse(responseStatusCode)
                    {
                        StatusMessage = responseEntry.StatusMessage,
                        Body = responseEntry.ResponsePayload,
                        Headers = deserializedHeaders
                    };

                    return(response);
                }).FirstOrDefault());

                // We'll only arrive here if we actually received a matching message. Otherwise
                // a TimeoutException will be thrown and will produce a timeout response.
                return(executionResponse);
            }
            catch (TimeoutException)
            {
                // Maximum wait time elapsed, create an empty response and return it.
                return(_traceContext.TraceTime("Create-Empty-Response", () => CreateTimeoutResponse(route, requestEntry.Id)));
            }
        }