コード例 #1
0
        DeleteClientScopeInfoAsync(ClientScopeInfo clientScopeInfo, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
        {
            var context = new SyncContext(Guid.NewGuid(), clientScopeInfo.Name);

            try
            {
                await using var runner = await this.GetConnectionAsync(context, SyncMode.Writing, SyncStage.ScopeWriting, 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)
                {
                    await this.InternalCreateScopeInfoTableAsync(context, DbScopeType.Client, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false);
                }

                bool isDeleted;
                // Write scopes locally
                (context, isDeleted) = await this.InternalDeleteClientScopeInfoAsync(clientScopeInfo, context, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false);

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

                return(isDeleted);
            }
            catch (Exception ex)
            {
                throw GetSyncError(context, ex);
            }
        }
コード例 #2
0
        public OperationArgs(SyncContext context, ServerScopeInfo serverScopeInfo, ClientScopeInfo clientScopeInfo, DbConnection connection = null, DbTransaction transaction = null)
            : base(context, connection, transaction)

        {
            this.ServerScopeInfo = serverScopeInfo;
            this.ClientScopeInfo = clientScopeInfo;
        }
コード例 #3
0
        private DbCommand InternalSetDeleteClientScopeInfoParameters(ClientScopeInfo clientScopeInfo, DbCommand command)
        {
            DbSyncAdapter.SetParameterValue(command, "sync_scope_id", clientScopeInfo.Id.ToString());
            DbSyncAdapter.SetParameterValue(command, "sync_scope_name", clientScopeInfo.Name);

            return(command);
        }
コード例 #4
0
        private async Task <Version> UpgdrateTo094Async(ClientScopeInfo clientScopeInfo, SyncContext context, DbConnection connection, DbTransaction transaction,
                                                        CancellationToken cancellationToken, IProgress <ProgressArgs> progress)
        {
            var newVersion = new Version(0, 9, 4);
            // Sorting tables based on dependencies between them

            var schemaTables = clientScopeInfo.Schema.Tables
                               .SortByDependencies(tab => tab.GetRelations()
                                                   .Select(r => r.GetParentTable()));

            var message = $"Upgrade to {newVersion}:";

            await this.InterceptAsync(new UpgradeProgressArgs(context, message, newVersion, connection, transaction), progress, cancellationToken).ConfigureAwait(false);

            var provision = SyncProvision.StoredProcedures | SyncProvision.Triggers;

            await this.DeprovisionAsync(clientScopeInfo.Name, provision, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

            var clientScope = await this.GetClientScopeInfoAsync(clientScopeInfo.Name, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

            var serverScope = new ServerScopeInfo
            {
                Schema  = clientScope.Schema,
                Setup   = clientScope.Setup,
                Version = clientScope.Version
            };

            clientScope = await this.ProvisionAsync(serverScope, provision, true, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

            return(newVersion);
        }
コード例 #5
0
 /// <summary>
 /// Make a shadow copy of an old scope to get the last sync information copied on this scope
 /// </summary>
 /// <param name="oldClientScopeInfo">old client scope that we we copy infos</param>
 public void ShadowScope(ClientScopeInfo oldClientScopeInfo)
 {
     this.LastServerSyncTimestamp = oldClientScopeInfo.LastServerSyncTimestamp;
     this.LastSyncTimestamp       = oldClientScopeInfo.LastSyncTimestamp;
     this.LastSync         = oldClientScopeInfo.LastSync;
     this.LastSyncDuration = oldClientScopeInfo.LastSyncDuration;
 }
コード例 #6
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);
            }
        }
コード例 #7
0
        InternalLoadClientScopeInfoAsync(SyncContext context, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress)
        {
            var scopeBuilder = this.GetScopeBuilder(this.Options.ScopeInfoTableName);

            using var command = scopeBuilder.GetCommandAsync(DbScopeCommandType.GetClientScopeInfo, connection, transaction);

            if (command == null)
            {
                return(context, null);
            }

            DbSyncAdapter.SetParameterValue(command, "sync_scope_name", context.ScopeName);

            var action = new ClientScopeInfoLoadingArgs(context, context.ScopeName, command, connection, transaction);

            await this.InterceptAsync(action, progress, cancellationToken).ConfigureAwait(false);

            if (action.Cancel || action.Command == null)
            {
                return(context, null);
            }

            await this.InterceptAsync(new DbCommandArgs(context, action.Command, connection, transaction), progress, cancellationToken).ConfigureAwait(false);

            using DbDataReader reader = await action.Command.ExecuteReaderAsync().ConfigureAwait(false);

            ClientScopeInfo clientScopeInfo = null;

            if (reader.Read())
            {
                clientScopeInfo = InternalReadClientScopeInfo(reader);
            }

            reader.Close();

            if (clientScopeInfo?.Schema != null)
            {
                clientScopeInfo.Schema.EnsureSchema();
            }

            if (clientScopeInfo != null)
            {
                var scopeLoadedArgs = new ClientScopeInfoLoadedArgs(context, context.ScopeName, clientScopeInfo, connection, transaction);
                await this.InterceptAsync(scopeLoadedArgs, progress, cancellationToken).ConfigureAwait(false);

                clientScopeInfo = scopeLoadedArgs.ClientScopeInfo;
            }

            action.Command.Dispose();

            return(context, clientScopeInfo);
        }
コード例 #8
0
        private async Task <Version> UpgdrateTo095Async(ClientScopeInfo scopeInfo, SyncContext context, DbConnection connection, DbTransaction transaction,
                                                        CancellationToken cancellationToken, IProgress <ProgressArgs> progress)

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

            var newVersion = new Version(0, 9, 5);

            await this.InterceptAsync(new UpgradeProgressArgs(context, $"Upgrade client scope {scopeInfo.Name} to {newVersion}:", newVersion, runner.Connection, runner.Transaction), runner.Progress, runner.CancellationToken).ConfigureAwait(false);

            // get scope info table name
            var parsedName = ParserName.Parse(this.Options.ScopeInfoTableName);
            var scopeClientInfoTableName = $"{parsedName.Unquoted().Normalized().ToString()}";

            var syncTable = new SyncTable(scopeClientInfoTableName);
            var scopeClientInfoTableBuilder = this.GetTableBuilder(syncTable, scopeInfo);
            var pkeys = await scopeClientInfoTableBuilder.GetPrimaryKeysAsync(runner.Connection, runner.Transaction).ConfigureAwait(false);

            if (pkeys.Count() == 1)
            {
                if (this.Provider.GetProviderTypeName().Contains("Dotmim.Sync.SqlServer.SqlSyncProvider"))
                {
                    var commandText = @$ "ALTER TABLE dbo.{scopeClientInfoTableName} DROP CONSTRAINT PK_{scopeClientInfoTableName};
                                        ALTER TABLE dbo.{scopeClientInfoTableName} ADD CONSTRAINT 
                                        PK_{scopeClientInfoTableName} PRIMARY KEY CLUSTERED (sync_scope_id, sync_scope_name);";

                    var command = runner.Connection.CreateCommand();
                    command.CommandText = commandText;
                    command.Transaction = runner.Transaction;
                    await command.ExecuteNonQueryAsync();

                    await this.InterceptAsync(new UpgradeProgressArgs(context, $"{scopeClientInfoTableName} primary keys updated on SQL Server", newVersion, runner.Connection, runner.Transaction), runner.Progress, runner.CancellationToken).ConfigureAwait(false);
                }

                if (this.Provider.GetProviderTypeName().Contains("Dotmim.Sync.MySql.MySqlSyncProvider"))
                {
                    var commandText = @$ "ALTER TABLE `{scopeClientInfoTableName}` 
                                        CHANGE COLUMN `sync_scope_name` `sync_scope_name` VARCHAR(100) NOT NULL ,
                                        DROP PRIMARY KEY,
                                        ADD PRIMARY KEY (`sync_scope_id`, `sync_scope_name`);";
                    var command     = runner.Connection.CreateCommand();
                    command.CommandText = commandText;
                    command.Transaction = runner.Transaction;
                    await command.ExecuteNonQueryAsync();
                }
                if (this.Provider.GetProviderTypeName().Contains("Dotmim.Sync.Sqlite.SqliteSyncProvider"))
                {
                    var commandText = @$ "
                                        PRAGMA foreign_keys=off;
                                        BEGIN TRANSACTION;

                                        ALTER TABLE [{scopeClientInfoTableName}] RENAME TO old_table_{scopeClientInfoTableName};
コード例 #9
0
        InternalSaveClientScopeInfoAsync(ClientScopeInfo clientScopeInfo, SyncContext context, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress)
        {
            var scopeBuilder = this.GetScopeBuilder(this.Options.ScopeInfoTableName);

            bool scopeExists;

            (context, scopeExists) = await InternalExistsClientScopeInfoAsync(clientScopeInfo.Name, context, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

            DbCommand command;

            if (scopeExists)
            {
                command = scopeBuilder.GetCommandAsync(DbScopeCommandType.UpdateClientScopeInfo, connection, transaction);
            }
            else
            {
                command = scopeBuilder.GetCommandAsync(DbScopeCommandType.InsertClientScopeInfo, connection, transaction);
            }

            if (command == null)
            {
                return(context, null);
            }

            command = InternalSetSaveClientScopeInfoParameters(clientScopeInfo, command);

            var action = new ScopeSavingArgs(context, scopeBuilder.ScopeInfoTableName.ToString(), DbScopeType.Client, clientScopeInfo, command, connection, transaction);

            await this.InterceptAsync(action, progress, cancellationToken).ConfigureAwait(false);

            if (action.Cancel || action.Command == null)
            {
                return(context, null);
            }

            await this.InterceptAsync(new DbCommandArgs(context, action.Command, connection, transaction), progress, cancellationToken).ConfigureAwait(false);

            using DbDataReader reader = await action.Command.ExecuteReaderAsync().ConfigureAwait(false);

            reader.Read();

            clientScopeInfo = InternalReadClientScopeInfo(reader);

            reader.Close();

            await this.InterceptAsync(new ScopeSavedArgs(context, scopeBuilder.ScopeInfoTableName.ToString(), DbScopeType.Client, clientScopeInfo, connection, transaction), progress, cancellationToken).ConfigureAwait(false);

            action.Command.Dispose();

            return(context, clientScopeInfo);
        }
コード例 #10
0
        private DbCommand InternalSetSaveClientScopeInfoParameters(ClientScopeInfo clientScopeInfo, DbCommand command)
        {
            DbSyncAdapter.SetParameterValue(command, "sync_scope_id", clientScopeInfo.Id.ToString());
            DbSyncAdapter.SetParameterValue(command, "sync_scope_name", clientScopeInfo.Name);
            DbSyncAdapter.SetParameterValue(command, "sync_scope_schema", clientScopeInfo.Schema == null ? DBNull.Value : (object)JsonConvert.SerializeObject(clientScopeInfo.Schema));
            DbSyncAdapter.SetParameterValue(command, "sync_scope_setup", clientScopeInfo.Setup == null ? DBNull.Value : (object)JsonConvert.SerializeObject(clientScopeInfo.Setup));
            DbSyncAdapter.SetParameterValue(command, "sync_scope_version", clientScopeInfo.Version);
            DbSyncAdapter.SetParameterValue(command, "scope_last_sync", clientScopeInfo.LastSync.HasValue ? (object)clientScopeInfo.LastSync.Value : DBNull.Value);
            DbSyncAdapter.SetParameterValue(command, "scope_last_sync_timestamp", clientScopeInfo.LastSyncTimestamp);
            DbSyncAdapter.SetParameterValue(command, "scope_last_server_sync_timestamp", clientScopeInfo.LastServerSyncTimestamp);
            DbSyncAdapter.SetParameterValue(command, "scope_last_sync_duration", clientScopeInfo.LastSyncDuration);

            return(command);
        }
コード例 #11
0
        InternalProvisionClientAsync(ServerScopeInfo serverScopeInfo, ClientScopeInfo clientScopeInfo, SyncContext context, SyncProvision provision = default, bool overwrite = true, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
        {
            try
            {
                if (serverScopeInfo.Schema == null)
                {
                    throw new Exception($"No Schema in your server scope info {serverScopeInfo.Name}");
                }

                if (serverScopeInfo.Schema == null)
                {
                    throw new Exception($"No Setup in your server scope info {serverScopeInfo.Name}");
                }

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

                // Check incompatibility with the flags
                if (provision.HasFlag(SyncProvision.ServerHistoryScope) || provision.HasFlag(SyncProvision.ServerScope))
                {
                    throw new InvalidProvisionForLocalOrchestratorException();
                }

                // 2) Provision
                if (provision == SyncProvision.None)
                {
                    provision = SyncProvision.Table | SyncProvision.StoredProcedures | SyncProvision.Triggers | SyncProvision.TrackingTable;
                }

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

                // set client scope setup and schema
                clientScopeInfo.Setup  = serverScopeInfo.Setup;
                clientScopeInfo.Schema = serverScopeInfo.Schema;

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

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

                return(context, clientScopeInfo);
            }
            catch (Exception ex)
            {
                throw GetSyncError(context, ex);
            }
        }
コード例 #12
0
        private ClientScopeInfo InternalReadClientScopeInfo(DbDataReader reader)
        {
            var clientScopeInfo = new ClientScopeInfo
            {
                Id       = reader.GetGuid(reader.GetOrdinal("sync_scope_id")),
                Name     = reader["sync_scope_name"] as string,
                Schema   = reader["sync_scope_schema"] == DBNull.Value ? null : JsonConvert.DeserializeObject <SyncSet>((string)reader["sync_scope_schema"]),
                Setup    = reader["sync_scope_setup"] == DBNull.Value ? null : JsonConvert.DeserializeObject <SyncSetup>((string)reader["sync_scope_setup"]),
                Version  = reader["sync_scope_version"] as string,
                LastSync = reader["scope_last_sync"] != DBNull.Value ? reader.GetDateTime(reader.GetOrdinal("scope_last_sync")) : null,
                LastServerSyncTimestamp = reader["scope_last_server_sync_timestamp"] != DBNull.Value ? (long?)reader.GetInt64(reader.GetOrdinal("scope_last_server_sync_timestamp")) : null,
                LastSyncTimestamp       = reader["scope_last_sync_timestamp"] != DBNull.Value ? (long?)reader.GetInt64(reader.GetOrdinal("scope_last_sync_timestamp")) : null,
                LastSyncDuration        = reader["scope_last_sync_duration"] != DBNull.Value ? reader.GetInt64(reader.GetOrdinal("scope_last_sync_duration")) : 0L
            };

            clientScopeInfo.IsNewScope = clientScopeInfo.LastSync == null;

            return(clientScopeInfo);
        }
コード例 #13
0
        InternalGetOperationAsync(ServerScopeInfo serverScopeInfo, ClientScopeInfo clientScopeInfo, SyncContext context, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
        {
            try
            {
                SyncOperation syncOperation = SyncOperation.Normal;

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

                var operationArgs = new OperationArgs(context, serverScopeInfo, clientScopeInfo, runner.Connection, runner.Transaction);

                await this.InterceptAsync(operationArgs, runner.Progress, runner.CancellationToken).ConfigureAwait(false);

                syncOperation = operationArgs.Operation;

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

                return(context, syncOperation);
            }
            catch (Exception ex)
            {
                throw GetSyncError(context, ex);
            }
        }
コード例 #14
0
        InternalApplySnapshotAsync(ClientScopeInfo clientScopeInfo, SyncContext context, BatchInfo serverBatchInfo, long clientTimestamp, long remoteClientTimestamp, DatabaseChangesSelected databaseChangesSelected,
                                   DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
        {
            try
            {
                if (serverBatchInfo == null)
                {
                    return(context, new DatabaseChangesApplied(), clientScopeInfo);
                }

                // Get context or create a new one
                context.SyncStage = SyncStage.SnapshotApplying;
                await this.InterceptAsync(new SnapshotApplyingArgs(context, this.Provider.CreateConnection()), progress, cancellationToken).ConfigureAwait(false);

                if (clientScopeInfo.Schema == null)
                {
                    throw new ArgumentNullException(nameof(clientScopeInfo.Schema));
                }

                // Applying changes and getting the new client scope info
                var(syncContext, changesApplied, newClientScopeInfo) = await this.InternalApplyChangesAsync(clientScopeInfo, context, serverBatchInfo,
                                                                                                            clientTimestamp, remoteClientTimestamp, ConflictResolutionPolicy.ServerWins, false, databaseChangesSelected, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

                await this.InterceptAsync(new SnapshotAppliedArgs(context, changesApplied), progress, cancellationToken).ConfigureAwait(false);

                // re-apply scope is new flag
                // to be sure we are calling the Initialize method, even for the delta
                // in that particular case, we want the delta rows coming from the current scope
                newClientScopeInfo.IsNewScope = true;

                return(context, changesApplied, newClientScopeInfo);
            }
            catch (Exception ex)
            {
                throw GetSyncError(context, ex);
            }
        }
コード例 #15
0
        InternalDeleteClientScopeInfoAsync(ClientScopeInfo clientScopeInfo, SyncContext context, DbConnection connection, DbTransaction transaction, CancellationToken cancellationToken, IProgress <ProgressArgs> progress)
        {
            var scopeBuilder = this.GetScopeBuilder(this.Options.ScopeInfoTableName);

            bool scopeExists;

            (context, scopeExists) = await InternalExistsClientScopeInfoAsync(clientScopeInfo.Name, context, connection, transaction, cancellationToken, progress).ConfigureAwait(false);

            if (!scopeExists)
            {
                return(context, true);
            }

            using var command = scopeBuilder.GetCommandAsync(DbScopeCommandType.DeleteClientScopeInfo, connection, transaction);

            InternalSetDeleteClientScopeInfoParameters(clientScopeInfo, command);

            var action = new ScopeSavingArgs(context, scopeBuilder.ScopeInfoTableName.ToString(), DbScopeType.Client, clientScopeInfo, command, connection, transaction);

            await this.InterceptAsync(action, progress, cancellationToken).ConfigureAwait(false);

            if (action.Cancel || action.Command == null)
            {
                return(context, false);
            }

            await this.InterceptAsync(new DbCommandArgs(context, action.Command, connection, transaction), progress, cancellationToken).ConfigureAwait(false);

            await action.Command.ExecuteNonQueryAsync().ConfigureAwait(false);

            await this.InterceptAsync(new ScopeSavedArgs(context, scopeBuilder.ScopeInfoTableName.ToString(), DbScopeType.Client, clientScopeInfo, connection, transaction), progress, cancellationToken).ConfigureAwait(false);

            action.Command.Dispose();

            return(context, true);
        }
コード例 #16
0
 public ClientScopeInfoLoadedArgs(SyncContext context, string scopeName, ClientScopeInfo clientScopeInfo, DbConnection connection = null, DbTransaction transaction = null)
     : base(context, connection, transaction)
 {
     this.ScopeName       = scopeName;
     this.ClientScopeInfo = clientScopeInfo;
 }
        InternalApplyThenGetChangesAsync(ClientScopeInfo clientScope, SyncContext context, BatchInfo clientBatchInfo, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
        {
            try
            {
                if (Provider == null)
                {
                    throw new MissingProviderException(nameof(InternalApplyThenGetChangesAsync));
                }

                long remoteClientTimestamp = 0L;
                DatabaseChangesSelected serverChangesSelected = null;
                DatabaseChangesApplied  clientChangesApplied  = null;
                BatchInfo  serverBatchInfo       = null;
                IScopeInfo serverClientScopeInfo = null;

                // Create two transactions
                // First one to commit changes
                // Second one to get changes now that everything is commited
                await using (var runner = await this.GetConnectionAsync(context, SyncMode.Writing, SyncStage.ChangesApplying, connection, transaction, cancellationToken, progress).ConfigureAwait(false))
                {
                    //Direction set to Upload
                    context.SyncWay = SyncWay.Upload;

                    // Getting server scope assumes we have already created the schema on server
                    // Scope name is the scope name coming from client
                    // Since server can have multiples scopes
                    (context, serverClientScopeInfo) = await this.InternalLoadServerScopeInfoAsync(context, runner.Connection, runner.Transaction, runner.CancellationToken, runner.Progress).ConfigureAwait(false);

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

                    // Deserialiaze schema
                    var schema = serverClientScopeInfo.Schema;

                    // Create message containing everything we need to apply on server side
                    var applyChanges = new MessageApplyChanges(Guid.Empty, clientScope.Id, false, clientScope.LastServerSyncTimestamp, schema, this.Options.ConflictResolutionPolicy,
                                                               this.Options.DisableConstraintsOnApplyChanges, this.Options.CleanMetadatas, this.Options.CleanFolder, false, clientBatchInfo);

                    // Call provider to apply changes
                    (context, clientChangesApplied) = await this.InternalApplyChangesAsync(serverClientScopeInfo, context, applyChanges, runner.Connection, runner.Transaction, runner.CancellationToken, runner.Progress).ConfigureAwait(false);

                    await this.InterceptAsync(new TransactionCommitArgs(context, runner.Connection, runner.Transaction), runner.Progress, runner.CancellationToken).ConfigureAwait(false);

                    // commit first transaction
                    await runner.CommitAsync().ConfigureAwait(false);
                }

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


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

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

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

                    // 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(serverClientScopeInfo, context, fromScratch, clientScope.LastServerSyncTimestamp, remoteClientTimestamp, clientScope.Id,
                                                           this.Provider.SupportsMultipleActiveResultSets,
                                                           this.Options.BatchDirectory, null, runner.Connection, runner.Transaction, runner.CancellationToken, runner.Progress).ConfigureAwait(false);

                    if (runner.CancellationToken.IsCancellationRequested)
                    {
                        runner.CancellationToken.ThrowIfCancellationRequested();
                    }

                    // generate the new scope item
                    this.CompleteTime = DateTime.UtcNow;

                    var scopeHistory = new ServerHistoryScopeInfo
                    {
                        Id   = clientScope.Id,
                        Name = clientScope.Name,
                        LastSyncTimestamp = remoteClientTimestamp,
                        LastSync          = this.CompleteTime,
                        LastSyncDuration  = this.CompleteTime.Value.Subtract(context.StartTime).Ticks,
                    };

                    // Write scopes locally
                    await this.InternalSaveServerHistoryScopeAsync(scopeHistory, context, runner.Connection, runner.Transaction, runner.CancellationToken, runner.Progress).ConfigureAwait(false);

                    // Commit second transaction for getting changes
                    await this.InterceptAsync(new TransactionCommitArgs(context, runner.Connection, runner.Transaction), runner.Progress, runner.CancellationToken).ConfigureAwait(false);

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

                var serverSyncChanges = new ServerSyncChanges(remoteClientTimestamp, serverBatchInfo, serverChangesSelected);

                return(context, serverSyncChanges, clientChangesApplied, this.Options.ConflictResolutionPolicy);
            }
            catch (Exception ex)
            {
                throw GetSyncError(context, ex);
            }
        }
コード例 #18
0
        /// <summary>
        /// Check
        /// </summary>
        public virtual async Task <(SyncContext, bool, ClientScopeInfo, ServerScopeInfo)> IsConflictingSetupAsync(SyncContext context, SyncSetup inputSetup, ClientScopeInfo clientScopeInfo, ServerScopeInfo serverScopeInfo, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
        {
            if (clientScopeInfo.IsNewScope || clientScopeInfo.Schema == null)
            {
                return(context, false, clientScopeInfo, serverScopeInfo);
            }

            if (inputSetup != null && clientScopeInfo.Setup != null && !clientScopeInfo.Setup.EqualsByProperties(inputSetup))
            {
                var conflictingSetupArgs = new ConflictingSetupArgs(context, inputSetup, clientScopeInfo, serverScopeInfo);
                await this.InterceptAsync(conflictingSetupArgs, progress, cancellationToken).ConfigureAwait(false);

                if (conflictingSetupArgs.Action == ConflictingSetupAction.Rollback)
                {
                    throw new Exception("Seems you are trying another Setup tables that what is stored in your client scope database. Please create a new scope or deprovision and provision again your client scope.");
                }
                if (conflictingSetupArgs.Action == ConflictingSetupAction.Abort)
                {
                    return(context, true, clientScopeInfo, serverScopeInfo);
                }

                // re affect scope infos
                clientScopeInfo = conflictingSetupArgs.ClientScopeInfo;
                serverScopeInfo = conflictingSetupArgs.ServerScopeInfo;
            }

            if (clientScopeInfo.Setup != null && serverScopeInfo.Setup != null && !clientScopeInfo.Setup.EqualsByProperties(serverScopeInfo.Setup))
            {
                var conflictingSetupArgs = new ConflictingSetupArgs(context, inputSetup, clientScopeInfo, serverScopeInfo);
                await this.InterceptAsync(conflictingSetupArgs, progress, cancellationToken).ConfigureAwait(false);

                if (conflictingSetupArgs.Action == ConflictingSetupAction.Rollback)
                {
                    throw new Exception("Seems your client setup is different from your server setup. Please create a new scope or deprovision and provision again your client scope with the server scope.");
                }
                if (conflictingSetupArgs.Action == ConflictingSetupAction.Abort)
                {
                    return(context, true, clientScopeInfo, serverScopeInfo);
                }

                // re affect scope infos
                clientScopeInfo = conflictingSetupArgs.ClientScopeInfo;
                serverScopeInfo = conflictingSetupArgs.ServerScopeInfo;
            }

            // We gave 2 chances to user to edit the setup and fill correct values.
            // Final check, but if not valid, raise an error
            if (clientScopeInfo.Setup != null && serverScopeInfo.Setup != null && !clientScopeInfo.Setup.EqualsByProperties(serverScopeInfo.Setup))
            {
                throw new Exception("Seems your client setup is different from your server setup. Please create a new scope or deprovision and provision again your client scope with the server scope.");
            }

            return(context, false, clientScopeInfo, serverScopeInfo);
        }
コード例 #19
0
 public ConflictingSetupArgs(SyncContext context, SyncSetup setup, ClientScopeInfo clientScopeInfo, ServerScopeInfo serverScopeInfo, DbConnection connection = null, DbTransaction transaction = null) : base(context, connection, transaction)
 {
     this.Setup           = setup;
     this.ClientScopeInfo = clientScopeInfo;
     this.ServerScopeInfo = serverScopeInfo;
 }
コード例 #20
0
        InternalApplyChangesAsync(ClientScopeInfo clientScopeInfo, SyncContext context, BatchInfo serverBatchInfo,
                                  long clientTimestamp, long remoteClientTimestamp, ConflictResolutionPolicy policy, bool snapshotApplied, DatabaseChangesSelected allChangesSelected,
                                  DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress <ProgressArgs> progress = null)
        {
            try
            {
                // lastSyncTS : apply lines only if they are not modified since last client sync
                var lastTimestamp = clientScopeInfo.LastSyncTimestamp;
                // isNew : if IsNew, don't apply deleted rows from server
                var isNew = clientScopeInfo.IsNewScope;
                // We are in downloading mode

                // Create the message containing everything needed to apply changes
                var applyChanges = new MessageApplyChanges(clientScopeInfo.Id, Guid.Empty, isNew, lastTimestamp, clientScopeInfo.Schema, policy,
                                                           this.Options.DisableConstraintsOnApplyChanges, this.Options.CleanMetadatas, this.Options.CleanFolder, snapshotApplied,
                                                           serverBatchInfo);

                DatabaseChangesApplied clientChangesApplied;

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

                context.SyncWay = SyncWay.Download;

                // Call apply changes on provider
                (context, clientChangesApplied) = await this.InternalApplyChangesAsync(clientScopeInfo, context, applyChanges, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false);

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

                // check if we need to delete metadatas
                if (this.Options.CleanMetadatas && clientChangesApplied.TotalAppliedChanges > 0 && lastTimestamp.HasValue)
                {
                    List <ClientScopeInfo> allScopes;
                    (context, allScopes) = await this.InternalLoadAllClientScopesInfoAsync(context, runner.Connection, runner.Transaction, runner.CancellationToken, runner.Progress).ConfigureAwait(false);

                    if (allScopes.Count > 0)
                    {
                        // Get the min value from LastSyncTimestamp from all scopes
                        var minLastTimeStamp = allScopes.Min(scope => scope.LastSyncTimestamp.HasValue ? scope.LastSyncTimestamp.Value : Int64.MaxValue);
                        minLastTimeStamp = minLastTimeStamp > lastTimestamp.Value ? lastTimestamp.Value : minLastTimeStamp;

                        (context, _) = await this.InternalDeleteMetadatasAsync(allScopes, context, minLastTimeStamp, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false);
                    }
                }

                // now the sync is complete, remember the time
                this.CompleteTime = DateTime.UtcNow;

                // generate the new scope item
                clientScopeInfo.IsNewScope              = false;
                clientScopeInfo.LastSync                = this.CompleteTime;
                clientScopeInfo.LastSyncTimestamp       = clientTimestamp;
                clientScopeInfo.LastServerSyncTimestamp = remoteClientTimestamp;
                clientScopeInfo.LastSyncDuration        = this.CompleteTime.Value.Subtract(context.StartTime).Ticks;

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

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

                return(context, clientChangesApplied, clientScopeInfo);
            }
            catch (Exception ex)
            {
                throw GetSyncError(context, ex);
            }
        }