internal async Task <(SyncContext, BatchInfo, ChangesSelected)> GetChanges(SyncContext context, ScopeInfo scopeInfo)
        {
            BatchInfo batchInfo = null;

            try
            {
                Debug.WriteLine("GetChanges called: _syncBatchProducer is null");

                var configuration = GetCacheConfiguration();

                // Check if the remote is not outdated
                var isOutdated = this.IsRemoteOutdated();

                // Get a chance to make the sync even if it's outdated
                if (isOutdated && this.SyncOutdated != null)
                {
                    Debug.WriteLine("Raising Sync Remote Outdated Event");
                    var outdatedEventArgs = new OutdatedEventArgs();
                    this.SyncOutdated(this, outdatedEventArgs);
                    Debug.WriteLine($"Action taken : {outdatedEventArgs.Action.ToString()}");

                    if (outdatedEventArgs.Action == OutdatedSyncAction.PartialSync)
                    {
                        Debug.WriteLine("Attempting Partial Sync");
                    }
                }


                // the sync is still outdated, abort it
                if (isOutdated)
                {
                    Debug.WriteLine("Aborting Sync");
                    return(context, null, null);
                }

                // Statistics about changes that are selected
                ChangesSelected changesSelected;

                // if we try a Reinitialize action, don't get any changes from client
                // else get changes from batch or in memory methods
                if (context.SyncWay == SyncWay.Upload && context.SyncType == SyncType.Reinitialize)
                {
                    (batchInfo, changesSelected) = this.GetEmptyChanges(context, scopeInfo);
                }
                else if (configuration.DownloadBatchSizeInKB == 0)
                {
                    (batchInfo, changesSelected) = await this.EnumerateChangesInternal(context, scopeInfo);
                }
                else
                {
                    (batchInfo, changesSelected) = await this.EnumerateChangesInBatchesInternal(context, scopeInfo);
                }

                return(context, batchInfo, changesSelected);
            }
            catch (Exception)
            {
                throw;
            }
        }
        /// <summary>
        /// Gets a batch of changes to synchronize when given batch size,
        /// destination knowledge, and change data retriever parameters.
        /// </summary>
        /// <returns>A DbSyncContext object that will be used to retrieve the modified data.</returns>
        public virtual async Task <(SyncContext, BatchInfo, ChangesSelected)> GetChangeBatchAsync(SyncContext context, ScopeInfo scopeInfo)
        {
            try
            {
                if (scopeInfo == null)
                {
                    throw new ArgumentNullException("scopeInfo", "Client scope info is null");
                }

                var configuration = GetCacheConfiguration();

                // Check if the provider is not outdated
                var isOutdated = this.IsRemoteOutdated();

                // Get a chance to make the sync even if it's outdated
                if (isOutdated)
                {
                    var outdatedEventArgs = new OutdatedEventArgs();
                    this.SyncOutdated?.Invoke(this, outdatedEventArgs);

                    if (outdatedEventArgs.Action != OutdatedSyncAction.Rollback)
                    {
                        context.SyncType = outdatedEventArgs.Action == OutdatedSyncAction.Reinitialize ? SyncType.Reinitialize : SyncType.ReinitializeWithUpload;
                    }

                    if (outdatedEventArgs.Action == OutdatedSyncAction.Rollback)
                    {
                        throw new OutOfDateException("The provider is out of date ! Try to make a Reinitialize sync");
                    }
                }

                // batch info containing changes
                BatchInfo batchInfo;

                // Statistics about changes that are selected
                ChangesSelected changesSelected;

                // if we try a Reinitialize action, don't get any changes from client
                // else get changes from batch or in memory methods
                if (context.SyncWay == SyncWay.Upload && context.SyncType == SyncType.Reinitialize)
                {
                    (batchInfo, changesSelected) = this.GetEmptyChanges(context, scopeInfo);
                }
                else if (configuration.DownloadBatchSizeInKB == 0)
                {
                    (batchInfo, changesSelected) = await this.EnumerateChangesInternal(context, scopeInfo);
                }
                else
                {
                    (batchInfo, changesSelected) = await this.EnumerateChangesInBatchesInternal(context, scopeInfo);
                }

                return(context, batchInfo, changesSelected);
            }
            catch (Exception ex)
            {
                throw new SyncException(ex, SyncStage.TableChangesSelecting, this.ProviderTypeName);
            }
        }
        /// <summary>
        /// Gets a batch of changes to synchronize when given batch size,
        /// destination knowledge, and change data retriever parameters.
        /// </summary>
        /// <returns>A DbSyncContext object that will be used to retrieve the modified data.</returns>
        public virtual async Task <(SyncContext, BatchInfo, ChangesSelected)> GetChangeBatchAsync(
            SyncContext context, MessageGetChangesBatch message)
        {
            try
            {
                if (message.ScopeInfo == null)
                {
                    throw new ArgumentNullException("scopeInfo", "Client scope info is null");
                }

                // Check if the provider is not outdated
                var isOutdated = this.IsRemoteOutdated();

                // Get a chance to make the sync even if it's outdated
                if (isOutdated)
                {
                    var outdatedEventArgs = new OutdatedEventArgs();
                    this.SyncOutdated?.Invoke(this, outdatedEventArgs);

                    if (outdatedEventArgs.Action != OutdatedSyncAction.Rollback)
                    {
                        context.SyncType = outdatedEventArgs.Action == OutdatedSyncAction.Reinitialize ? SyncType.Reinitialize : SyncType.ReinitializeWithUpload;
                    }

                    if (outdatedEventArgs.Action == OutdatedSyncAction.Rollback)
                    {
                        throw new OutOfDateException("The provider is out of date ! Try to make a Reinitialize sync");
                    }
                }

                // create local directory
                if (this.Options.BatchSize > 0 && !string.IsNullOrEmpty(this.Options.BatchDirectory) && !Directory.Exists(this.Options.BatchDirectory))
                {
                    Directory.CreateDirectory(this.Options.BatchDirectory);
                }

                // batch info containing changes
                BatchInfo batchInfo;

                // Statistics about changes that are selected
                ChangesSelected changesSelected;

                // if we try a Reinitialize action, don't get any changes from client
                // else get changes from batch or in memory methods
                if (context.SyncWay == SyncWay.Upload && context.SyncType == SyncType.Reinitialize)
                {
                    (batchInfo, changesSelected) = this.GetEmptyChanges(context, message.ScopeInfo, this.Options.BatchSize, this.Options.BatchDirectory);
                }
                else if (this.Options.BatchSize == 0)
                {
                    (batchInfo, changesSelected) = await this.EnumerateChangesInternal(context, message.ScopeInfo, message.Schema, this.Options.BatchDirectory, message.Policy, message.Filters);
                }
                else
                {
                    (batchInfo, changesSelected) = await this.EnumerateChangesInBatchesInternal(context, message.ScopeInfo, this.Options.BatchSize, message.Schema, this.Options.BatchDirectory, message.Policy, message.Filters);
                }

                return(context, batchInfo, changesSelected);
            }
            catch (Exception ex)
            {
                throw new SyncException(ex, SyncStage.TableChangesSelecting, this.ProviderTypeName);
            }
        }