public override async Task <PurgeResult> PurgeInstanceStateAsync(PurgeInstanceFilter purgeInstanceFilter)
        {
            var purgeQuery = new SqlOrchestrationQuery
            {
                PageSize        = 1000,
                CreatedTimeFrom = purgeInstanceFilter.CreatedTimeFrom,
                FetchInput      = false,
                FetchOutput     = false,
            };

            if (purgeInstanceFilter.CreatedTimeTo != null)
            {
                purgeQuery.CreatedTimeTo = purgeInstanceFilter.CreatedTimeTo.Value;
            }

            if (purgeInstanceFilter.RuntimeStatus?.Any() == true)
            {
                purgeQuery.StatusFilter = new HashSet <OrchestrationStatus>(purgeInstanceFilter.RuntimeStatus);
            }

            IReadOnlyCollection <OrchestrationState> results = await this.GetManyOrchestrationsAsync(purgeQuery, CancellationToken.None);

            IEnumerable <string> instanceIds = results.Select(r => r.OrchestrationInstance.InstanceId);
            int purgedInstanceCount          = await this.PurgeOrchestrationHistoryAsync(instanceIds);

            return(new PurgeResult(purgedInstanceCount));
        }
        public override async Task <OrchestrationQueryResult> GetOrchestrationWithQueryAsync(OrchestrationQuery query, CancellationToken cancellationToken)
        {
            if (query.TaskHubNames?.Any() == true)
            {
                throw new NotSupportedException("Querying orchestrations by task hub name is not supported.");
            }

            var sqlOrchestrationQuery = new SqlOrchestrationQuery
            {
                CreatedTimeFrom  = query.CreatedTimeFrom.GetValueOrDefault(),
                CreatedTimeTo    = query.CreatedTimeTo.GetValueOrDefault(),
                FetchInput       = query.FetchInputsAndOutputs,
                FetchOutput      = query.FetchInputsAndOutputs,
                InstanceIdPrefix = query.InstanceIdPrefix,
                PageSize         = query.PageSize
            };

            if (query.RuntimeStatus?.Any() == true)
            {
                sqlOrchestrationQuery.StatusFilter = new HashSet <OrchestrationStatus>(query.RuntimeStatus);
            }

            // The continuation token is just a page number.
            if (string.IsNullOrWhiteSpace(query.ContinuationToken))
            {
                sqlOrchestrationQuery.PageNumber = 0;
            }
            else if (int.TryParse(query.ContinuationToken, out int pageNumber))
            {
                sqlOrchestrationQuery.PageNumber = pageNumber;
            }
            else
            {
                throw new ArgumentException($"The continuation token '{query.ContinuationToken}' is invalid.", nameof(query));
            }

            IReadOnlyCollection <OrchestrationState> results = await this.GetManyOrchestrationsAsync(sqlOrchestrationQuery, cancellationToken);

            string?continuationToken =
                results.Count == sqlOrchestrationQuery.PageSize ? (sqlOrchestrationQuery.PageNumber + 1).ToString() : null;

            return(new OrchestrationQueryResult(results, continuationToken));
        }
        /// <summary>
        /// Queries the database for all orchestration instances that match a given filter.
        /// </summary>
        /// <param name="query">The query parameters to use as a filter.</param>
        /// <param name="cancellationToken">A token for cancelling the query.</param>
        /// <returns>Returns a collection of <see cref="OrchestrationState"/> objects for orchestrations that match the specified query filter.</returns>
        public async Task <IReadOnlyCollection <OrchestrationState> > GetManyOrchestrationsAsync(SqlOrchestrationQuery query, CancellationToken cancellationToken)
        {
            if (query.PageSize < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(query), $"{nameof(query.PageSize)} must be a positive number.");
            }

            if (query.PageNumber < 0 || query.PageNumber > short.MaxValue)
            {
                throw new ArgumentOutOfRangeException(nameof(query), $"{nameof(query.PageNumber)} must be between 0 and {short.MaxValue} (inclusive).");
            }

            SqlDateTime createdTimeFrom = query.CreatedTimeFrom.ToSqlUtcDateTime(SqlDateTime.MinValue);
            SqlDateTime createdTimeTo   = query.CreatedTimeTo.ToSqlUtcDateTime(SqlDateTime.MaxValue);

            using SqlConnection connection = await this.GetAndOpenConnectionAsync(cancellationToken);

            using SqlCommand command = this.GetSprocCommand(connection, "dt._QueryManyOrchestrations");

            command.Parameters.Add("@PageSize", SqlDbType.SmallInt).Value                   = query.PageSize;
            command.Parameters.Add("@PageNumber", SqlDbType.SmallInt).Value                 = query.PageNumber;
            command.Parameters.Add("@FetchInput", SqlDbType.Bit).Value                      = query.FetchInput;
            command.Parameters.Add("@FetchOutput", SqlDbType.Bit).Value                     = query.FetchOutput;
            command.Parameters.Add("@CreatedTimeFrom", SqlDbType.DateTime).Value            = createdTimeFrom;
            command.Parameters.Add("@CreatedTimeTo", SqlDbType.DateTime).Value              = createdTimeTo;
            command.Parameters.Add("@InstanceIDPrefix", SqlDbType.VarChar, size: 100).Value = query.InstanceIdPrefix ?? SqlString.Null;

            if (query.StatusFilter?.Count > 0)
            {
                string filter = string.Join(",", query.StatusFilter);
                command.Parameters.Add("@RuntimeStatusFilter", SqlDbType.VarChar, size: 200).Value = filter;
            }

            using DbDataReader reader = await SqlUtils.ExecuteReaderAsync(
                      command,
                      this.traceHelper,
                      instanceId : null,
                      cancellationToken);

            var results = new List <OrchestrationState>(query.PageSize);

            while (await reader.ReadAsync(cancellationToken))
            {
                OrchestrationState state = reader.GetOrchestrationState();
                results.Add(state);
            }

            return(results);
        }