Пример #1
0
        // 9
        /// <summary>
        /// Creates a synchronization agent that will handle a full synchronization between a client and a server.
        /// </summary>
        /// <param name="clientProvider">local provider to your client database</param>
        /// <param name="remoteOrchestrator">Remote Orchestrator already configured with a SyncProvider</param>
        /// <param name="options">Sync Options defining options used by your local provider (and remote provider if type of remoteOrchestrator is not a WebRemoteOrchestrator)</param>
        public SyncAgent(CoreProvider clientProvider, RemoteOrchestrator remoteOrchestrator, SyncOptions options = default)
            : this()
        {
            if (clientProvider is null)
            {
                throw new ArgumentNullException(nameof(clientProvider));
            }
            if (remoteOrchestrator is null)
            {
                throw new ArgumentNullException(nameof(remoteOrchestrator));
            }
            if (options == default)
            {
                options = new SyncOptions();
            }

            // Override remote orchestrator options, setup and scope name
            remoteOrchestrator.Options = options;

            var localOrchestrator = new LocalOrchestrator(clientProvider, options);

            this.LocalOrchestrator  = localOrchestrator;
            this.RemoteOrchestrator = remoteOrchestrator;
            this.EnsureOptionsAndSetupInstances();
        }
Пример #2
0
 /// <summary>
 /// Create an agent where the remote orchestrator could be of type RemoteOrchestrator (TCP connection) or WebClientOrchestrator (Http connection)
 /// </summary>
 /// <param name="clientProvider">local provider to your client database</param>
 /// <param name="remoteOrchestrator">remote orchestrator : RemoteOrchestrator or WebClientOrchestrator) </param>
 /// <param name="setup">Contains list of your tables. Not used if remote orchestrator is WebClientOrchestrator</param>
 /// <param name="options">Options. Only used on locally if remote orchestrator is WebClientOrchestrator</param>
 public SyncAgent(CoreProvider clientProvider, IRemoteOrchestrator remoteOrchestrator,
                  SyncSetup setup = null, SyncOptions options = null)
     : this(new LocalOrchestrator(clientProvider), remoteOrchestrator, setup, options)
 {
 }
Пример #3
0
        /// <summary>
        /// Launch a synchronization with the specified mode
        /// </summary>
        public async Task <SyncContext> SynchronizeAsync(SyncType syncType, CancellationToken cancellationToken, IProgress <ProgressArgs> progress = null)
        {
            // for view purpose, if needed
            if (this.LocalOrchestrator?.Provider != null)
            {
                this.LocalOrchestrator.Provider.Options = this.Options;
            }

            if (this.RemoteOrchestrator?.Provider != null)
            {
                this.RemoteOrchestrator.Provider.Options = this.Options;
            }

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

            this.SessionState = SyncSessionState.Synchronizing;
            this.SessionStateChanged?.Invoke(this, this.SessionState);

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

                ScopeInfo scope = null;

                // Starts sync by :
                // - Getting local config we have set by code
                // - Ensure local scope is created (table and values)
                (context, scope) = await this.LocalOrchestrator.EnsureScopeAsync
                                       (context, this.Setup.ScopeName, this.Options.ScopeInfoTableName, cancellationToken, progress);

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

                // check if we already have a schema from local
                if (!string.IsNullOrEmpty(scope.Schema))
                {
                    this.Schema = JsonConvert.DeserializeObject <SyncSet>(scope.Schema);
                }
                else
                {
                    // FIRST call to server
                    // Get the server scope info and server reference id to local scope
                    // Be sure options / schema from client are passed if needed
                    // Then the configuration with full schema
                    var serverSchema = await this.RemoteOrchestrator.EnsureSchemaAsync(
                        context, this.Setup, cancellationToken, remoteProgress);

                    context     = serverSchema.context;
                    this.Schema = serverSchema.schema;
                }


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

                // on local orchestrator, get local changes
                // Most probably the schema has changed, so we passed it again (coming from Server)
                // Don't need to pass again Options since we are not modifying it between server and client
                var clientChanges = await this.LocalOrchestrator.GetChangesAsync(
                    context, this.Schema, scope, this.Options.BatchSize, this.Options.BatchDirectory, cancellationToken, progress);

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

                // set context
                context = clientChanges.context;

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

                // IF is new and we have a snapshot directory, try to apply a snapshot
                if (fromScratch)
                {
                    // Get snapshot files
                    var serverSnapshotChanges = await this.RemoteOrchestrator.GetSnapshotAsync(
                        context, scope, this.Schema, this.Options.SnapshotsDirectory, this.Options.BatchDirectory, cancellationToken, remoteProgress);

                    if (serverSnapshotChanges.serverBatchInfo != null && serverSnapshotChanges.serverBatchInfo.HasData())
                    {
                        context = await this.LocalOrchestrator.ApplySnapshotAndGetChangesAsync(context, scope, this.Schema, serverSnapshotChanges.serverBatchInfo,
                                                                                               clientChanges.clientTimestamp, serverSnapshotChanges.remoteClientTimestamp, this.Options.DisableConstraintsOnApplyChanges,
                                                                                               this.Options.BatchSize, this.Options.BatchDirectory, this.Options.UseBulkOperations, this.Options.CleanMetadatas, this.Options.ScopeInfoTableName,
                                                                                               cancellationToken, progress);
                    }
                }

                var serverChanges = await this.RemoteOrchestrator.ApplyThenGetChangesAsync(
                    context, scope, this.Schema, clientChanges.clientBatchInfo, this.Options.DisableConstraintsOnApplyChanges, this.Options.UseBulkOperations,
                    this.Options.CleanMetadatas, this.Options.CleanFolder, this.Options.BatchSize, this.Options.BatchDirectory,
                    this.Options.ConflictResolutionPolicy, cancellationToken, remoteProgress);

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

                // set context
                context = serverChanges.context;
                // Serialize schema to be able to save it in client db
                if (string.IsNullOrEmpty(scope.Schema))
                {
                    var schemaLight = JsonConvert.SerializeObject(this.Schema);
                    scope.Schema = schemaLight;
                }

                // Policy is always Server policy, so reverse this policy to get the client policy
                var clientPolicy = this.Options.ConflictResolutionPolicy == ConflictResolutionPolicy.ServerWins ? ConflictResolutionPolicy.ClientWins : ConflictResolutionPolicy.ServerWins;

                var localChanges = await this.LocalOrchestrator.ApplyChangesAsync(
                    context, scope, this.Schema, serverChanges.serverBatchInfo,
                    clientPolicy, clientChanges.clientTimestamp, serverChanges.remoteClientTimestamp,
                    this.Options.DisableConstraintsOnApplyChanges, this.Options.UseBulkOperations,
                    this.Options.CleanMetadatas, this.Options.ScopeInfoTableName, true,
                    cancellationToken, progress);

                context.TotalChangesDownloaded += localChanges.clientChangesApplied.TotalAppliedChanges;
                context.TotalChangesUploaded   += clientChanges.clientChangesSelected.TotalChangesSelected;
                context.TotalSyncErrors        += localChanges.clientChangesApplied.TotalAppliedChangesFailed;


                if (cancellationToken.IsCancellationRequested)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }
            }
            catch (SyncException se)
            {
                Debug.WriteLine($"Sync Exception: {se.Message}. TypeName:{se.TypeName}.");
                throw;
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"Unknwon Exception: {ex.Message}.");
                throw new SyncException(ex, SyncStage.None);
            }
            finally
            {
                // End the current session
                this.SessionState = SyncSessionState.Ready;
                this.SessionStateChanged?.Invoke(this, this.SessionState);
            }

            return(context);
        }
Пример #4
0
 /// <summary>
 /// Create a local orchestrator, used to orchestrates the whole sync on the client side
 /// </summary>
 public LocalOrchestrator(CoreProvider provider, SyncOptions options, SyncSetup setup, string scopeName = SyncOptions.DefaultScopeName)
     : base(provider, options, setup, scopeName)
 {
 }