locked
Issue with SyncProgress event raised by SqlSyncprovider or DbSyncProvider RRS feed

  • Question

  • This applies to SqlSyncProvider and DBSyncProvider which likely means it is in RelationalSyncProvider which is from where these derive and where this event originates.

    In the syncing from a remote provider to a local provider (i.e. downloading), this event is raised after ever table with stage=SelectingChanges. In the first table's progress information (e.Scopeprogress), the TotalChangesApplied will be 0 and the TotalChanges will be the changes selected for that table. For subsequent tables, the TotalChanges applied is still 0 and the TotalChanges is the sum so far (i.e. if there were 10, 20 and 30 changes in the first 3 tables, this number would be 10, 30 and 60 respectively, so the pair of numbers in the 3 events are 0/10, 0/30 and 0/60).

    So far so good.

    Now the next thing that happens is that the changes start to be applied. After each apply, another SyncProcess event is raised. The stage is ApplyingUpdates. The TotalChangesApplied increments for each update applied. This is fine too. The problem is the value of the TotalChanges. This is always set to the number of changes in the first table. So, following on the 10, 20, 30 example, the numbers reported for the 60 events are:
    0/10, 1/10, 2/10, ... 9/10, 10/10, 11/10, 12/10,..... 60/10.

    So why does this matter? Well, number one, a TotalChangesApplied of 60 out of a TotalChanges of 10 does not make sense. But more importantly (and how I discovered this), it certainly looks like this is an obvious mechanism for populating a ProgressBar control (the first number being the Value property and the second being the Maximum property).

    To make matters worse, what I have described is not quite how it works. You see, I have seen the situation where there were 3 tables with no changes except for the last one, and the numbers are correct for this. (selecting: 0/0, 0/0, 0/4; applied: 1/4, 2/4, 3/4, 4/4), so maybe the number saved is the first non-zero value during selection and not the sum.

    Has anybody else come across this and is there a fix or workaround (other than tracking the value returned in the selecting phase and using that in the applying phase instead of the value returned?

    TIA,

    Steve
    Saturday, March 13, 2010 4:22 AM

Answers

All replies

    • Marked as answer by Speedware Monday, March 15, 2010 2:36 PM
    Saturday, March 13, 2010 8:03 AM
  • June,

    Yes, that is it. I have developed a kludge workaround for it as follows:

            #region SyncProgess.TotalChanges Kludge
    
            /// <summary>
            /// Int variable used to handle a bug in the SyncProgress TotalChanges value
            /// on outgoing changes. This variable will be used to accumulate the largest
            /// value of TotalChanges that is sent for a stage of SelectingChanges and will
            /// be used as the TotalChanges value in events that have stage ApplyingInserts
            /// or ApplyingUpdates or ApplyingDeletes. This is a known bug as is acknowledged at a link from here:
            /// http://social.microsoft.com/Forums/en/syncdevdiscussions/thread/b0211430-63d4-4446-a40b-6ca1856df6a8
            /// </summary>
            private int _kludgeSyncProgressTotalChanges;
    
            private void KludgeSyncProgressInitialize()
            {
                this._kludgeSyncProgressTotalChanges = 0;
            }
    
            private void KludgeSyncProgressSelectingChanges(DbSyncStage stage, DbSyncScopeProgress scopeProgress)
            {
                if (stage != DbSyncStage.SelectingChanges)
                    return;
    
                // Save the largest value in a selecting changes event
                if (scopeProgress.TotalChanges > this._kludgeSyncProgressTotalChanges)
                    this._kludgeSyncProgressTotalChanges = scopeProgress.TotalChanges;
            }
    
            private int KludgeSyncProgressTotalChanges(SyncOrchestratorState state, DbSyncScopeProgress scopeProgress)
            {
                // When going to server:
                // substitute the largest value found in a selecting changes event into any applying event.
                if (state == SyncOrchestratorState.Uploading)
                    return this._kludgeSyncProgressTotalChanges;
    
                // When going from to client, only the first file has the right number, meaning that
                // TotalChangesApplied <= TotalChanges. After that the TotalChanges never changes but
                // the TotalChangesApplied continues to be increment. Our kludge will be to add 20% to the
                // TotalChangesApplied. Obviously when sync is done, this needs to be fixed. This is done by
                // directly updating the control.
    
                if (scopeProgress.TotalChangesApplied > scopeProgress.TotalChanges)
                {
                    if (this._kludgeSyncProgressTotalChanges < scopeProgress.TotalChangesApplied)
                        this._kludgeSyncProgressTotalChanges = ((120 * scopeProgress.TotalChangesApplied) / 100) + 1;
    
                    return this._kludgeSyncProgressTotalChanges;
                }
    
                return scopeProgress.TotalChanges;
            }
    
            #endregion
    

    And how it is called...

            void syncProvider_SyncProgress(object sender, DbSyncProgressEventArgs e)
            {
                if (e == null)
                    return;
    
                this.KludgeSyncProgressSelectingChanges(e.Stage, e.ScopeProgress);
    
                this.UpdateProgress(this.syncOrchestrator.State, e.ScopeProgress);
            }
    
            void syncOrchestrator_StateChanged(object sender, SyncOrchestratorStateChangedEventArgs e)
            {
                // Initialize our kludge that handles the bug in SyncProgress reporting
                this.KludgeSyncProgressInitialize();
            }
    
            private void UpdateProgress(SyncOrchestrator state, DbSyncScopeProgress progress)
            {
    
                int totalChanges = this.KludgeSyncProgressTotalChanges(state, progress);
                int changes = progress.TotalChangesApplied;
    
                if (progressBar.Value > totalChanges)
                    progressBar.Value = 0; // avoid fault setting maximum next.
                progressBar.Maximum = totalChanges;
                progressBar.Value = changes;
    Application.DoEvents(); }

    // when the sync has finished, you need to update the final progress state to 100% done since you
    // might be up to 20% short.



    The above is not quite how I did it - I carry my own state where I track the uploading or downloading. So I am not quite sure if SyncOrchestrator.State will only show Uploading and Downloading states during the sync. It might show UploadingAndDownloading or DownloadingAndUploading in which case you will also need to track that.

    The result of this is that for the uploading side of things, you have a progressbar that will act normally since you know the maximum value of selected changes (the kludge tracks it). For the downloading side, the progressbar will go to 100% then back off to 80%, then go up to 100%, back down to 80% and so forth until finally ending at 100%. For small numbers of updates, this is not noticable. For large (say many thousands), the action seems quite normal, much like many windows operations that discover they have more to do (like copying files etc).

    Anyway, the gist of it is that you can get around this bug.

    YMMV.

    Monday, March 15, 2010 2:36 PM