/// <summary>
        /// Monitors a <see cref="CloudTask"/> collection until each of its members has reached a desired state at least once.
        /// </summary>
        /// <remarks>
        /// <para>
        /// The state of each <see cref="CloudTask"/> instance is assumed to be authoritative at the time of the call.
        /// Instances that are already at the <paramref name="desiredState"/> are ignored.
        /// The <see cref="CloudTask"/> instances in the collection are treated as read-only.
        /// This means that when the call completes (timeout or not) the <see cref="CloudTask"/> instances should be refreshed before using.
        /// </para>
        /// <para>
        /// This method runs asynchronously.
        /// </para>
        /// </remarks>
        /// <param name="tasksToMonitor">The collection of tasks to monitor.</param>
        /// <param name="desiredState">The target state of the tasks. The method will exit when all tasks have reached this state at least once.</param>
        /// <param name="timeout">The maximum amount of time this call will wait before timing out.</param>
        /// <param name="controlParams">Controls various settings of the monitor, such as delay between each poll.</param>
        /// <param name="additionalBehaviors">A collection of <see cref="BatchClientBehavior"/> instances that are applied to the Batch service request after the <see cref="CustomBehaviors"/>.</param>
        /// <returns>A <see cref="System.Threading.Tasks.Task"/> that represents the asynchronous operation.</returns>
        /// <exception cref="TimeoutException">Thrown if the <paramref name="timeout"/> has elapsed.</exception>
        public async Task WhenAll(
            IEnumerable <CloudTask> tasksToMonitor,
            Common.TaskState desiredState,
            TimeSpan timeout,
            ODATAMonitorControl controlParams = null,
            IEnumerable <BatchClientBehavior> additionalBehaviors = null)
        {
            using (CancellationTokenSource tokenSource = new CancellationTokenSource(timeout))
            {
                try
                {
                    await this.WhenAllImplAsync(
                        tasksToMonitor,
                        desiredState,
                        tokenSource.Token,
                        controlParams,
                        additionalBehaviors).ConfigureAwait(continueOnCapturedContext: false);
                }
                catch (OperationCanceledException cancellationException)
                {
                    if (cancellationException.CancellationToken == tokenSource.Token)
                    {
                        throw new TimeoutException(
                                  string.Format(CultureInfo.InvariantCulture, BatchErrorMessages.ODataMonitorTimedOut, timeout),
                                  cancellationException);
                    }

                    throw;
                }
            }
        }
        /// <summary>
        /// Monitors a <see cref="CloudTask"/> collection until each of its members has reached a desired state at least once.
        /// </summary>
        /// <remarks>
        /// <para>
        /// The state of each <see cref="CloudTask"/> instance is assumed to be authoritative at the time of the call.
        /// Instances that are already at the <paramref name="desiredState"/> are ignored.
        /// The <see cref="CloudTask"/> instances in the collection are treated as read-only.
        /// This means that when the call completes (timeout or not) the <see cref="CloudTask"/> instances should be refreshed before using.
        /// </para>
        /// <para>
        /// This is a blocking operation. For a non-blocking equivalent, see
        /// <see cref="WhenAll(System.Collections.Generic.IEnumerable{Microsoft.Azure.Batch.CloudTask},Microsoft.Azure.Batch.Common.TaskState,System.TimeSpan,Microsoft.Azure.Batch.ODATAMonitorControl,System.Collections.Generic.IEnumerable{Microsoft.Azure.Batch.BatchClientBehavior})"/>.
        /// </para>
        /// </remarks>
        /// <param name="tasksToMonitor">The collection of tasks to monitor.</param>
        /// <param name="desiredState">The target state of the tasks. The method will exit when all tasks have reached this state at least once.</param>
        /// <param name="timeout">The maximum amount of time this call will wait before timing out.</param>
        /// <param name="controlParams">Controls various settings of the monitor, such as delay between each poll.</param>
        /// <param name="additionalBehaviors">A collection of <see cref="BatchClientBehavior"/> instances that are applied to the Batch service request after the <see cref="CustomBehaviors"/>.</param>
        /// <exception cref="TimeoutException">Thrown if the <paramref name="timeout"/> has elapsed.</exception>
        public void WaitAll(
            IEnumerable <CloudTask> tasksToMonitor,
            Common.TaskState desiredState,
            TimeSpan timeout,
            ODATAMonitorControl controlParams = null,
            IEnumerable <BatchClientBehavior> additionalBehaviors = null)
        {
            Task asyncTask = this.WhenAll(tasksToMonitor, desiredState, timeout, controlParams, additionalBehaviors);

            asyncTask.WaitAndUnaggregateException(this.CustomBehaviors, additionalBehaviors);
        }
 /// <summary>
 /// Monitors a <see cref="CloudTask"/> collection until each of its members has reached a desired state at least once.
 /// </summary>
 /// <remarks>
 /// <para>
 /// The state of each <see cref="CloudTask"/> instance is assumed to be authoritative at the time of the call.
 /// Instances that are already at the <paramref name="desiredState"/> are ignored.
 /// The <see cref="CloudTask"/> instances in the collection are treated as read-only.
 /// This means that when the call completes (timeout or not) the <see cref="CloudTask"/> instances should be refreshed before using.
 /// </para>
 /// <para>
 /// This method runs asynchronously.
 /// </para>
 /// </remarks>
 /// <param name="tasksToMonitor">The collection of tasks to monitor.</param>
 /// <param name="desiredState">The target state of the tasks. The method will exit when all tasks have reached this state at least once.</param>
 /// <param name="cancellationToken">A <see cref="CancellationToken"/> for controlling the lifetime of the asynchronous operation.</param>
 /// <param name="controlParams">Controls various settings of the monitor, such as delay between each poll.</param>
 /// <param name="additionalBehaviors">A collection of <see cref="BatchClientBehavior"/> instances that are applied to the Batch service request after the <see cref="CustomBehaviors"/>.</param>
 /// <returns>A <see cref="System.Threading.Tasks.Task"/> that represents the asynchronous operation.</returns>
 /// <exception cref="OperationCanceledException">Thrown if the <paramref name="cancellationToken"/> was cancelled.</exception>
 public async Task WhenAll(
     IEnumerable <CloudTask> tasksToMonitor,
     Common.TaskState desiredState,
     CancellationToken cancellationToken,
     ODATAMonitorControl controlParams = null,
     IEnumerable <BatchClientBehavior> additionalBehaviors = null)
 {
     await this.WhenAllImplAsync(
         tasksToMonitor,
         desiredState,
         cancellationToken,
         controlParams,
         additionalBehaviors).ConfigureAwait(continueOnCapturedContext: false);
 }
            internal RefreshViaODATAFilterList(
                CancellationToken cancellationToken,
                ODATADetailLevel odataDetail,
                Func <T, bool> condition,
                ListDelegate <T> listObjects,
                ODATAMonitorControl odataMonitorControl)
            {
                this.CancellationToken = cancellationToken;
                _odataDetailLevel      = odataDetail;
                _condition             = condition;
                _listObjects           = listObjects;
                _odataMonitorControl   = odataMonitorControl;

                CurrentPassQueue = new Queue <MonitorLastCall <T> >();
                NextPassQueue    = new Queue <MonitorLastCall <T> >();
            }
        /// <summary>
        /// Polls the collection of instances until each has passed the condition at least once.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="collectionToMonitor"></param>
        /// <param name="condition"></param>
        /// <param name="getName"></param>
        /// <param name="listObjects"></param>
        /// <param name="cancellationToken"></param>
        /// <param name="detailLevel">Controls the detail level of the data returned by a call to the Azure Batch Service.</param>
        /// <param name="control"></param>
        /// <returns></returns>
        public static async Task WhenAllAsync <T>(
            IEnumerable <T> collectionToMonitor,
            Func <T, bool> condition,
            Func <T, string> getName,
            ListDelegate <T> listObjects,
            CancellationToken cancellationToken,
            ODATADetailLevel detailLevel,
            ODATAMonitorControl control) where T : IRefreshable
        {
            if (null == collectionToMonitor)
            {
                throw new ArgumentNullException("collectionToMonitor");
            }

            if (null == condition)
            {
                throw new ArgumentNullException("condition");
            }

            RefreshViaODATAFilterList <T> odataRefresher = new RefreshViaODATAFilterList <T>(cancellationToken, detailLevel, condition, listObjects, control);

            // populate for first pass
            foreach (T curT in collectionToMonitor)
            {
                // filter out the instances that already meet the condition
                if (!condition(curT))
                {
                    MonitorLastCall <T> box = new MonitorLastCall <T>(curT, /* flags each instance as available to refresh "now" */ DateTime.MinValue);

                    odataRefresher.CurrentPassQueue.Enqueue(box);
                }
            }

            // process the instances in the current pass... swap queues to begin next pass
            while (!odataRefresher.CancellationToken.IsCancellationRequested &&
                   odataRefresher.CurrentPassQueue.Count > 0)
            {
                // get next instance
                MonitorLastCall <T> nextInstanceToProcess = odataRefresher.CurrentPassQueue.Dequeue();

                // build an odata filter with as many names as the limit will allow and call refresh instances as needed
                Task asyncProcessOneInstance = odataRefresher.ProcessOneInstance(nextInstanceToProcess, getName);

                // a-wait for completion
                await asyncProcessOneInstance.ConfigureAwait(continueOnCapturedContext : false);

                // if the current pass queue is empty, swap the queues to begin next pass
                if (0 == odataRefresher.CurrentPassQueue.Count)
                {
                    odataRefresher.SwapQueues();

                    // if we appear to be done, the stringbuilder might have the last predicate in it so flush that
                    if (0 == odataRefresher.CurrentPassQueue.Count)
                    {
                        // start the call to process the last predicate
                        Task asyncListTask = odataRefresher.CallListAndProcessResults();

                        // a-wait for completion
                        await asyncListTask.ConfigureAwait(continueOnCapturedContext : false);

                        // if the last predicate created new work... the swap will bring it into a new pass
                        odataRefresher.SwapQueues();
                    }
                }
            }

            //Were we cancelled?
            odataRefresher.CancellationToken.ThrowIfCancellationRequested();
        }
        private async Task WhenAllImplAsync(
            IEnumerable <CloudTask> tasksToMonitor,
            Common.TaskState desiredState,
            CancellationToken cancellationToken,
            ODATAMonitorControl controlParams,
            IEnumerable <BatchClientBehavior> additionalBehaviors)
        {
            if (null == tasksToMonitor)
            {
                throw new ArgumentNullException("tasksToMonitor");
            }

            // we only need the id and state for this monitor.  the filter clause will be updated by the monitor
            ODATADetailLevel odataSuperOptimalPredicates = new ODATADetailLevel()
            {
                SelectClause = "id,state"
            };

            // for validation and list calls we need the parent name values
            string jobId = null;

            // set up behaviors
            BehaviorManager bhMgr = new BehaviorManager(this.CustomBehaviors, additionalBehaviors);

            // set up control params if needed
            if (null == controlParams)
            {
                controlParams = new ODATAMonitorControl(); // use defaults
            }

            tasksToMonitor = await UtilitiesInternal.EnumerateIfNeededAsync(tasksToMonitor, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);

            // validation: job schedule id and jobId
            foreach (CloudTask curTask in tasksToMonitor)
            {
                // can only monitor bound objects
                if (curTask.BindingState != BindingState.Bound)
                {
                    Exception ex = UtilitiesInternal.OperationForbiddenOnUnboundObjects;

                    throw ex;
                }

                // set or validate job Id
                if (null == jobId)
                {
                    jobId = curTask.ParentJobId;
                }
                else
                {
                    // all instances must have same parent
                    if (!jobId.Equals(curTask.ParentJobId, StringComparison.OrdinalIgnoreCase))
                    {
                        Exception ex = UtilitiesInternal.MonitorRequiresConsistentHierarchyChain;

                        throw ex;
                    }
                }
            }

            // start call
            Task asyncTask = ODATAMonitor.WhenAllAsync(
                tasksToMonitor,
                x =>
            {
                // return true if is desired state
                bool hasReachedDesiredState = x.State == desiredState;
                return(hasReachedDesiredState);
            },
                x => { return(x.Id); },                                                                                          // return the Id of the task
                () => _parentUtilities.ParentBatchClient.JobOperations.ListTasksImpl(jobId, bhMgr, odataSuperOptimalPredicates), // call this lambda to (re)fetch the list
                cancellationToken,
                odataSuperOptimalPredicates,
                controlParams);

            await asyncTask.ConfigureAwait(continueOnCapturedContext : false);
        }