Exemple #1
0
        GetChangesAsync(ClientScopeInfo clientScope, SyncParameters parameters = default, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
        {
            var context = new SyncContext(Guid.NewGuid(), clientScope.Name);

            if (parameters != null)
            {
                context.Parameters = parameters;
            }

            try
            {
                await using var runner = await this.GetConnectionAsync(context, SyncMode.Reading, SyncStage.ChangesSelecting, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                // Before getting changes, be sure we have a remote schema available
                ServerScopeInfo serverScopeInfo;
                (context, serverScopeInfo) = await this.InternalGetServerScopeInfoAsync(context, clientScope.Setup, runner.Connection, runner.Transaction, cancellationToken, progress);

                // TODO : if serverScope.Schema is null, should we Provision here ?

                // Should we ?
                if (serverScopeInfo.Schema == null)
                {
                    throw new MissingRemoteOrchestratorSchemaException();
                }

                //Direction set to Download
                context.SyncWay = SyncWay.Download;

                // Output
                // JUST Before get changes, get the timestamp, to be sure to
                // get rows inserted / updated elsewhere since the sync is not over
                long remoteClientTimestamp;
                (context, remoteClientTimestamp) = await this.InternalGetLocalTimestampAsync(context, runner.Connection, runner.Transaction, cancellationToken, progress);

                // Get if we need to get all rows from the datasource
                var fromScratch = clientScope.IsNewScope || context.SyncType == SyncType.Reinitialize || context.SyncType == SyncType.ReinitializeWithUpload;

                BatchInfo serverBatchInfo;
                DatabaseChangesSelected serverChangesSelected;
                // When we get the chnages from server, we create the batches if it's requested by the client
                // the batch decision comes from batchsize from client
                (context, serverBatchInfo, serverChangesSelected) =
                    await this.InternalGetChangesAsync(clientScope, context, fromScratch, clientScope.LastServerSyncTimestamp, remoteClientTimestamp,
                                                       clientScope.Id, this.Provider.SupportsMultipleActiveResultSets,
                                                       this.Options.BatchDirectory, null, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false);

                await runner.CommitAsync().ConfigureAwait(false);

                return(new ServerSyncChanges(remoteClientTimestamp, serverBatchInfo, serverChangesSelected));
            }
            catch (Exception ex)
            {
                throw GetSyncError(context, ex);
            }
        }
Exemple #2
0
        /// <summary>
        /// Create a snapshot, based on the Setup object.
        /// </summary>
        /// <param name="syncParameters">if not parameters are found in the SyncContext instance, will use thes sync parameters instead</param>
        /// <returns>Instance containing all information regarding the snapshot</returns>
        public virtual Task <BatchInfo> CreateSnapshotAsync(SyncParameters syncParameters = null, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
        => RunInTransactionAsync(SyncStage.SnapshotCreating, async(ctx, connection, transaction) =>
        {
            if (string.IsNullOrEmpty(this.Options.SnapshotsDirectory) || this.Options.BatchSize <= 0)
            {
                throw new SnapshotMissingMandatariesOptionsException();
            }

            // check parameters
            // If context has no parameters specified, and user specifies a parameter collection we switch them
            if ((ctx.Parameters == null || ctx.Parameters.Count <= 0) && syncParameters != null && syncParameters.Count > 0)
            {
                ctx.Parameters = syncParameters;
            }

            // 1) Get Schema from remote provider
            var schema = await this.InternalGetSchemaAsync(ctx, this.Setup, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

            // 2) Ensure databases are ready
            var provision = SyncProvision.TrackingTable | SyncProvision.StoredProcedures | SyncProvision.Triggers;

            // 3) Provision everything
            var scopeBuilder = this.GetScopeBuilder(this.Options.ScopeInfoTableName);

            var exists = await this.InternalExistsScopeInfoTableAsync(ctx, DbScopeType.Server, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

            if (!exists)
            {
                await this.InternalCreateScopeInfoTableAsync(ctx, DbScopeType.Server, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);
            }

            var serverScopeInfo = await this.InternalGetScopeAsync <ServerScopeInfo>(ctx, DbScopeType.Server, this.ScopeName, scopeBuilder, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

            schema = await InternalProvisionAsync(ctx, false, schema, this.Setup, provision, serverScopeInfo, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

            // 4) Getting the most accurate timestamp
            var remoteClientTimestamp = await this.InternalGetLocalTimestampAsync(ctx, connection, transaction, cancellationToken, progress);

            await this.InterceptAsync(new SnapshotCreatingArgs(ctx, schema, this.Options.SnapshotsDirectory, this.Options.BatchSize, remoteClientTimestamp, connection, transaction), cancellationToken).ConfigureAwait(false);

            // 5) Create the snapshot
            var batchInfo = await this.InternalCreateSnapshotAsync(ctx, schema, this.Setup, connection, transaction, remoteClientTimestamp, cancellationToken, progress).ConfigureAwait(false);

            var snapshotCreated = new SnapshotCreatedArgs(ctx, batchInfo, connection);
            await this.InterceptAsync(snapshotCreated, cancellationToken).ConfigureAwait(false);
            this.ReportProgress(ctx, progress, snapshotCreated);


            return(batchInfo);
        }, connection, transaction, cancellationToken);
Exemple #3
0
        GetChangesAsync(string scopeName = SyncOptions.DefaultScopeName, SyncParameters parameters = default, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
        {
            var context = new SyncContext(Guid.NewGuid(), scopeName);

            if (parameters != null)
            {
                context.Parameters = parameters;
            }

            try
            {
                await using var runner = await this.GetConnectionAsync(context, SyncMode.Reading, SyncStage.ChangesSelecting, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                bool exists;
                (context, exists) = await this.InternalExistsScopeInfoTableAsync(context, DbScopeType.Client, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false);

                if (!exists)
                {
                    return(default);
Exemple #4
0
        /// <summary>
        /// Create a snapshot, based on the Setup object.
        /// </summary>
        /// <param name="syncParameters">if not parameters are found in the SyncContext instance, will use thes sync parameters instead</param>
        /// <returns>Instance containing all information regarding the snapshot</returns>
        public virtual async Task <BatchInfo> CreateSnapshotAsync(string scopeName, SyncSetup setup   = null, SyncParameters syncParameters        = null,
                                                                  DbConnection connection             = default, DbTransaction transaction         = default,
                                                                  CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
        {
            var context = new SyncContext(Guid.NewGuid(), scopeName);

            try
            {
                await using var runner = await this.GetConnectionAsync(context, SyncMode.Writing, SyncStage.SnapshotCreating, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                if (string.IsNullOrEmpty(this.Options.SnapshotsDirectory) || this.Options.BatchSize <= 0)
                {
                    throw new SnapshotMissingMandatariesOptionsException();
                }

                // check parameters
                // If context has no parameters specified, and user specifies a parameter collection we switch them
                if ((context.Parameters == null || context.Parameters.Count <= 0) && syncParameters != null && syncParameters.Count > 0)
                {
                    context.Parameters = syncParameters;
                }

                // 1) Get Schema from remote provider
                ServerScopeInfo serverScopeInfo;
                (context, serverScopeInfo) = await this.InternalGetServerScopeInfoAsync(context, setup, false, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false);

                // If we just have create the server scope, we need to provision it
                if (serverScopeInfo != null && serverScopeInfo.IsNewScope)
                {
                    // 2) Provision
                    var provision = SyncProvision.TrackingTable | SyncProvision.StoredProcedures | SyncProvision.Triggers;

                    (context, _) = await this.InternalProvisionAsync(serverScopeInfo, context, false, provision, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false);

                    // Write scopes locally
                    (context, serverScopeInfo) = await this.InternalSaveServerScopeInfoAsync(serverScopeInfo, context, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false);
                }

                // 4) Getting the most accurate timestamp
                long remoteClientTimestamp;
                (context, remoteClientTimestamp) = await this.InternalGetLocalTimestampAsync(context,
                                                                                             runner.Connection, runner.Transaction, runner.CancellationToken, runner.Progress).ConfigureAwait(false);

                // 5) Create the snapshot with
                BatchInfo batchInfo;

                (context, batchInfo) = await this.InternalCreateSnapshotAsync(serverScopeInfo, context, remoteClientTimestamp,
                                                                              runner.Connection, runner.Transaction, runner.CancellationToken, runner.Progress).ConfigureAwait(false);

                await runner.CommitAsync().ConfigureAwait(false);

                return(batchInfo);
            }
            catch (Exception ex)
            {
                throw GetSyncError(context, ex);
            }
        }
Exemple #5
0
 public virtual Task <BatchInfo> CreateSnapshotAsync(SyncSetup setup                     = null, SyncParameters syncParameters = null,
                                                     DbConnection connection             = default, DbTransaction transaction  = default,
                                                     CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
 => CreateSnapshotAsync(SyncOptions.DefaultScopeName, setup, syncParameters, connection, transaction, cancellationToken, progress);
Exemple #6
0
        /// <summary>
        /// Launch a synchronization with the specified mode
        /// </summary>
        public async Task <SyncResult> SynchronizeAsync(string scopeName, SyncSetup setup, SyncType syncType, SyncParameters parameters, CancellationToken cancellationToken, IProgress <ProgressArgs> progress = null)
        {
            // checkpoints dates
            var startTime    = DateTime.UtcNow;
            var completeTime = DateTime.UtcNow;

            // Create a logger
            var logger = this.Options.Logger ?? new SyncLogger().AddDebug();

            // Lock sync to prevent multi call to sync at the same time
            LockSync();

            // Context, used to back and forth data between servers
            var context = new SyncContext(Guid.NewGuid(), scopeName)
            {
                // if any parameters, set in context
                Parameters = parameters,
                // set sync type (Normal, Reinitialize, ReinitializeWithUpload)
                SyncType = syncType
            };


            // Result, with sync results stats.
            var result = new SyncResult(context.SessionId)
            {
                // set start time
                StartTime    = startTime,
                CompleteTime = completeTime,
            };

            this.SessionState = SyncSessionState.Synchronizing;
            this.SessionStateChanged?.Invoke(this, this.SessionState);
            //await Task.Run(async () =>
            //{
            try
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }

                if (setup != null)
                {
                    var remoteOrchestratorType = this.RemoteOrchestrator.GetType();
                    var providerType           = remoteOrchestratorType.Name;
                    if (providerType.ToLowerInvariant() == "webclientorchestrator" || providerType.ToLowerInvariant() == "webremotetorchestrator")
                    {
                        throw new Exception("Do not set Tables (or SyncSetup) from your client. Please use SyncAgent, without any Tables or SyncSetup. The tables will come from the server side");
                    }
                }

                // Begin session
                context = await this.LocalOrchestrator.InternalBeginSessionAsync(context, cancellationToken, progress).ConfigureAwait(false);

                if (cancellationToken.IsCancellationRequested)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }


                // no need to check on every call to SynchronizeAsync
                if (!checkUpgradeDone)
                {
                    var needToUpgrade = await this.LocalOrchestrator.NeedsToUpgradeAsync(default, default, cancellationToken, progress).ConfigureAwait(false);
 /// <summary>
 /// Create a snapshot, based on the Setup object.
 /// </summary>
 /// <param name="syncParameters">if not parameters are found in the SyncContext instance, will use thes sync parameters instead</param>
 /// <returns>Instance containing all information regarding the snapshot</returns>
 public virtual Task <BatchInfo> CreateSnapshotAsync(SyncParameters syncParameters        = null,
                                                     ISerializerFactory serializerFactory = default,
                                                     DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
 => RunInTransactionAsync(SyncStage.SnapshotCreating, async(ctx, connection, transaction) =>
Exemple #8
0
 /// <summary>
 /// Launch a Synchronization based on a named scope
 /// </summary>
 /// <param name="scopeName">Named scope</param>
 /// <param name="parameters">Parameters values for each of your setup filters</param>
 /// <param name="progress">IProgress instance to get a progression status during sync</param>
 /// <returns>Computed sync results</returns>
 public Task <SyncResult> SynchronizeAsync(string scopeName, SyncParameters parameters, IProgress <ProgressArgs> progress = null)
 => SynchronizeAsync(scopeName, null, SyncType.Normal, parameters, CancellationToken.None, progress);
Exemple #9
0
 /// <summary>
 /// Launch a Synchronization based on scope DefaultScope
 /// </summary>
 /// <param name="syncType">Synchronization mode: Normal, Reinitialize or ReinitializeWithUpload</param>
 /// <param name="parameters">Parameters values for each of your setup filters</param>
 /// <param name="progress">IProgress instance to get a progression status during sync</param>
 /// <returns>Computed sync results</returns>
 public Task <SyncResult> SynchronizeAsync(SyncType syncType, SyncParameters parameters, IProgress <ProgressArgs> progress = null)
 => SynchronizeAsync(SyncOptions.DefaultScopeName, null, syncType, parameters, CancellationToken.None, progress);
Exemple #10
0
 /// <summary>
 /// Launch a Synchronization based on a named scope
 /// </summary>
 /// <param name="scopeName">Named scope</param>
 /// <param name="setup">Setup instance containing the table list and optionnally columns</param>
 /// <param name="syncType">Synchronization mode: Normal, Reinitialize or ReinitializeWithUpload</param>
 /// <param name="parameters">Parameters values for each of your setup filters</param>
 /// <param name="progress">IProgress instance to get a progression status during sync</param>
 /// <returns>Computed sync results</returns>
 public Task <SyncResult> SynchronizeAsync(string scopeName, SyncSetup setup, SyncType syncType, SyncParameters parameters, IProgress <ProgressArgs> progress = default)
 => SynchronizeAsync(scopeName, setup, syncType, parameters, CancellationToken.None, progress);
Exemple #11
0
 /// <summary>
 /// Launch a Synchronization based on scope DefaultScope
 /// </summary>
 /// <param name="setup">Setup instance containing the table list and optionnally columns</param>
 /// <param name="parameters">Parameters values for each of your setup filters</param>
 /// <param name="progress">IProgress instance to get a progression status during sync</param>
 /// <returns>Computed sync results</returns>
 public Task <SyncResult> SynchronizeAsync(SyncSetup setup, SyncParameters parameters, IProgress <ProgressArgs> progress = default)
 => SynchronizeAsync(SyncOptions.DefaultScopeName, setup, SyncType.Normal, parameters, CancellationToken.None, progress);
Exemple #12
0
 /// <summary>
 /// Launch a Synchronization based on a named scope
 /// </summary>
 /// <param name="scopeName">Named scope</param>
 /// <param name="tables">Tables list to synchronize</param>
 /// <param name="parameters">Parameters values for each of your setup filters</param>
 /// <param name="progress">IProgress instance to get a progression status during sync</param>
 /// <returns>Computed sync results</returns>
 public Task <SyncResult> SynchronizeAsync(string scopeName, string[] tables, SyncParameters parameters, IProgress <ProgressArgs> progress = default) =>
 SynchronizeAsync(scopeName, new SyncSetup(tables), SyncType.Normal, parameters, CancellationToken.None, progress);