public void StopJobsRunningTest() { var jobTest = new TestJob(); var progress = new SynchronousProgress <ProgressInfo>(); var token = (new CancellationTokenSource()).Token; var jobID = jobClient.Add(appID, () => jobTest.Start("Hello World", progress, token)); //run job jobServer.RunJobs(new List <string> { jobID }); Thread.Sleep(1000); var job = jobClient.GetJob(jobID); Assert.IsNotNull(job); Assert.AreEqual(JobStatus.Running, job.Status); jobClient.SetCommandStop(new List <string> { jobID }); jobServer.StopJobs(); //stop running job Thread.Sleep(3000); job = jobClient.GetJob(jobID); Assert.AreEqual(JobStatus.Stopped, job.Status); jobClient.DeleteJobs(new List <string>() { jobID }); }
public async Task Combined_IncrementalProgressUpdates() { var discovery = PartDiscovery.Combine(TestUtilities.V2Discovery, TestUtilities.V1Discovery); var assemblies = new[] { typeof(AssemblyDiscoveryTests.DiscoverablePart1).GetTypeInfo().Assembly, this.GetType().GetTypeInfo().Assembly, }; DiscoveryProgress lastReceivedUpdate = default(DiscoveryProgress); int progressUpdateCount = 0; var progress = new SynchronousProgress <DiscoveryProgress>(update => { progressUpdateCount++; Assert.NotNull(update.Status); ////Assert.True(update.Completion >= lastReceivedUpdate.Completion); // work can be discovered that regresses this legitimately Assert.True(update.Completion <= 1); Assert.True(update.Status != lastReceivedUpdate.Status || update.Completion != lastReceivedUpdate.Completion); this.output.WriteLine( "Completion reported: {0} ({1}/{2}): {3}", update.Completion, update.CompletedSteps, update.TotalSteps, update.Status); lastReceivedUpdate = update; }); await discovery.CreatePartsAsync(assemblies, progress); progress.RethrowAnyExceptions(); Assert.True(lastReceivedUpdate.Completion > 0); Assert.True(progressUpdateCount > 2); }
private static async Task SynchronizeAsync() { // Database script used for this sample : https://github.com/Mimetis/Dotmim.Sync/blob/master/CreateAdventureWorks.sql // Create a web proxy Orchesrtrator with a custom serializer var serverProxyOrchestrator = new WebRemoteOrchestrator("https://localhost:44342/api/sync") { Converter = new CustomConverter(), SerializerFactory = new CustomMessagePackSerializerFactory() }; // Second provider is using plain old Sql Server provider, relying on triggers and tracking tables to create the sync environment var clientProvider = new SqlSyncProvider(clientConnectionString); var progress = new SynchronousProgress <ProgressArgs>(args => Console.WriteLine($"{args.Context.SyncStage}:\t{args.Message}")); // Creating an agent that will handle all the process var agent = new SyncAgent(clientProvider, serverProxyOrchestrator); do { // Launch the sync process var s1 = await agent.SynchronizeAsync(progress); // Write results Console.WriteLine(s1); } while (Console.ReadKey().Key != ConsoleKey.Escape); Console.WriteLine("End"); }
private static async Task CreateSnapshotAsync() { // [Required]: Get a connection string to your server data source var connectionString = configuration.GetSection("ConnectionStrings")["SqlConnection"]; // [Required] Tables involved in the sync process: var tables = new string[] { "ProductCategory", "ProductModel", "Product", "Address", "Customer", "CustomerAddress", "SalesOrderHeader", "SalesOrderDetail" }; var syncOptions = new SyncOptions { SnapshotsDirectory = Path.Combine(SyncOptions.GetDefaultUserBatchDiretory(), "Snapshots"), BatchSize = 2000, }; var syncSetup = new SyncSetup(tables); // Using the Progress pattern to handle progession during the synchronization var progress = new SynchronousProgress <ProgressArgs>(s => Console.WriteLine($"{s.Source}:\t{s.Message}")); var provider = new SqlSyncProvider(connectionString); var orchestrator = new RemoteOrchestrator(provider, syncOptions, syncSetup); var snap = await orchestrator.CreateSnapshotAsync(progress : progress); }
private static async Task SynchronizeAsync() { // Database script used for this sample : https://github.com/Mimetis/Dotmim.Sync/blob/master/CreateAdventureWorks.sql var serverOrchestrator = new WebClientOrchestrator("https://localhost.fiddler:44342/api/sync"); // Second provider is using plain old Sql Server provider, relying on triggers and tracking tables to create the sync environment var clientProvider = new SqlSyncProvider(clientConnectionString); // Creating an agent that will handle all the process var agent = new SyncAgent(clientProvider, serverOrchestrator); do { try { var progress = new SynchronousProgress <ProgressArgs>(args => Console.WriteLine($"{args.ProgressPercentage:p}:\t{args.Message}")); // Launch the sync process var s1 = await agent.SynchronizeAsync(progress); // Write results Console.WriteLine(s1); } catch (Exception ex) { Console.WriteLine(ex.Message); } } while (Console.ReadKey().Key != ConsoleKey.Escape); Console.WriteLine("End"); }
public static async Task SynchronizeLogsAsync(WebRemoteOrchestrator serverOrchestrator, SqlSyncProvider clientProvider, SyncOptions options, bool reinitialize = false) { // Creating an agent that will handle all the process var agent = new SyncAgent(clientProvider, serverOrchestrator, options); var progress = new SynchronousProgress <ProgressArgs>( pa => Console.WriteLine($"{pa.ProgressPercentage:p}\t {pa.Message}")); var syncType = reinitialize ? SyncType.Reinitialize : SyncType.Normal; try { // Launch the sync process var s1 = await agent.SynchronizeAsync("logs", syncType, progress); // Write results Console.WriteLine(s1); } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.WriteLine("End"); }
public static async Task SynchronizeDefaultAsync(WebRemoteOrchestrator serverOrchestrator, SqlSyncProvider clientProvider, SyncOptions options, bool reinitialize = false) { var agent = new SyncAgent(clientProvider, serverOrchestrator, options); var parameters = new SyncParameters { { "City", "Toronto" }, { "postal", DBNull.Value } }; var syncType = reinitialize ? SyncType.Reinitialize : SyncType.Normal; var progress = new SynchronousProgress <ProgressArgs>( pa => Console.WriteLine($"{pa.ProgressPercentage:p}\t {pa.Message}")); try { // Launch the sync process var s1 = await agent.SynchronizeAsync(syncType, progress); // Write results Console.WriteLine(s1); } catch (Exception ex) { Console.WriteLine(ex.Message); } }
private static async Task SynchronizeAsync() { // Database script used for this sample : https://github.com/Mimetis/Dotmim.Sync/blob/master/CreateAdventureWorks.sql var serverOrchestrator = new WebClientOrchestrator("https://localhost.fiddler:44342/api/sync"); var clientProvider = new SqlSyncProvider(clientConnectionString); var clientOptions = new SyncOptions { BatchSize = 500 }; var progress = new SynchronousProgress <ProgressArgs>(pa => Console.WriteLine($"{pa.Context.SessionId} - {pa.Context.SyncStage}\t {pa.Message}")); var agent = new SyncAgent(clientProvider, serverOrchestrator, clientOptions); do { // Launch the sync process var s1 = await agent.SynchronizeAsync(progress); // Write results Console.WriteLine(s1); } while (Console.ReadKey().Key != ConsoleKey.Escape); Console.WriteLine("End"); }
private async static Task TestVbSetup() { var serverProvider = new SqlSyncProvider(DbHelper.GetDatabaseConnectionString(serverDbName)); var clientProvider = new SqlSyncProvider(DbHelper.GetDatabaseConnectionString(clientDbName)); // Create the setup used for your sync process var tables = new string[] { "[vbs].VBSetup", }; // [Optional] : database setup var syncSetup = new SyncSetup(tables) { // optional : StoredProceduresPrefix = "sync_", StoredProceduresSuffix = "", TrackingTablesPrefix = "sync_", TrackingTablesSuffix = "", ScopeName = "SStGMobile_Sync", TriggersPrefix = "sync", }; var syncAgent = new SyncAgent(clientProvider, serverProvider, syncSetup); // Using the IProgress<T> pattern to handle progession dring the synchronization // Be careful, Progress<T> is not synchronous. Use SynchronousProgress<T> instead ! var progress = new SynchronousProgress <ProgressArgs>(s => Console.WriteLine($"{s.Context.SyncStage}:\t{s.Message}")); var syncContext = await syncAgent.SynchronizeAsync(progress); }
public async Task CleanUpTest() { //Test StopJobs with CleanUp() calls var jobTest = new TestJob(); var progress = new SynchronousProgress <ProgressInfo>(); var token = (new CancellationTokenSource()).Token; var jobID = await jobClient.AddAsync(appID, () => jobTest.Start("Hello World", progress, token)); //run job await jobServer.RunJobsAsync(new List <string> { jobID }); Thread.Sleep(1000); var job = await jobClient.GetJobAsync(jobID); Assert.IsNotNull(job); Assert.AreEqual(JobStatus.Running, job.Status); await jobClient.SetCommandStopAsync(new List <string> { jobID }); await jobServer.CleanUpAsync(); Thread.Sleep(3000); job = await jobClient.GetJobAsync(jobID); Assert.AreEqual(JobStatus.Stopped, job.Status); await jobClient.DeleteJobsAsync(new List <string>() { jobID }); }
public override async Task DoAsync(CancellationToken cancellationToken) { Logger.Trace($"{_repository} Downloading {Path}"); var progress = new SynchronousProgress <ulong>(); progress.ProgressChanged += (_, count) => _done += count; await _repository.DownloadTo(Path, Storage, progress, cancellationToken); }
protected async Task <GenerationResult> GenerateAsync(SourceText inputSource) { var solution = this.solution.WithDocumentText(this.inputDocumentId, inputSource); var inputDocument = solution.GetDocument(this.inputDocumentId); var generatorDiagnostics = new List <Diagnostic>(); var progress = new SynchronousProgress <Diagnostic>(generatorDiagnostics.Add); var inputCompilation = (CSharpCompilation)await inputDocument.Project.GetCompilationAsync(); var inputSyntaxTree = await inputDocument.GetSyntaxTreeAsync(); var outputSyntaxTree = await DocumentTransform.TransformAsync(inputCompilation, inputSyntaxTree, null, Assembly.Load, progress); var outputDocument = inputDocument.Project .AddDocument("output.cs", outputSyntaxTree.GetRoot()); // Make sure the result compiles without errors or warnings. var compilation = await outputDocument.Project.GetCompilationAsync(); var compilationDiagnostics = compilation.GetDiagnostics(); SourceText outputDocumentText = await outputDocument.GetTextAsync(); this.logger.WriteLine("{0}", outputDocumentText); // Verify all line endings are consistent (otherwise VS can bug the heck out of the user if they have the generated file open). string firstLineEnding = null; foreach (var line in outputDocumentText.Lines) { string actualNewLine = line.Text.GetSubText(TextSpan.FromBounds(line.End, line.EndIncludingLineBreak)).ToString(); if (firstLineEnding == null) { firstLineEnding = actualNewLine; } else if (actualNewLine != firstLineEnding && actualNewLine.Length > 0) { string expected = EscapeLineEndingCharacters(firstLineEnding); string actual = EscapeLineEndingCharacters(actualNewLine); Assert.True(false, $"Expected line ending characters '{expected}' but found '{actual}' on line {line.LineNumber + 1}.\nContent: {line}"); } } var semanticModel = await outputDocument.GetSemanticModelAsync(); var result = new GenerationResult(outputDocument, semanticModel, generatorDiagnostics, compilationDiagnostics); foreach (var diagnostic in generatorDiagnostics) { this.logger.WriteLine(diagnostic.ToString()); } foreach (var diagnostic in result.CompilationDiagnostics) { this.logger.WriteLine(diagnostic.ToString()); } return(result); }
private static void AddJob() { var job = new TestJob(); var progress = new SynchronousProgress <ProgressInfo>(); //just a place holder will be replaced by progress object from the server var cancelToken = (new CancellationTokenSource()).Token; //just a place holder will be replaced by Token object from the server var pauseToken = (new PauseTokenSource()).Token; //just a place holder will be replaced by Token object from the server var jobID = jobClient.Add("Shift.Demo.Client", () => job.Start("Hello World!", progress, cancelToken, pauseToken)); addedJobIDs.Add(jobID); Console.WriteLine(); Console.WriteLine("==> New job added to Shift DB table"); }
public async Task ContinueJobsPausedAsyncTest() { var jobTest = new TestJob(); var progress = new SynchronousProgress <ProgressInfo>(); var cancelToken = (new CancellationTokenSource()).Token; var pauseToken = (new PauseTokenSource()).Token; var jobID = await jobClient.AddAsync(AppID, () => jobTest.Start("Hello World", progress, cancelToken, pauseToken)); //run job await jobServer.RunJobsAsync(new List <string> { jobID }); Thread.Sleep(1000); await jobClient.SetCommandPauseAsync(new List <string> { jobID }); await jobServer.PauseJobsAsync(); //pause running job Thread.Sleep(3000); var job = await jobClient.GetJobAsync(jobID); Assert.NotNull(job); Assert.Equal(JobStatus.Paused, job.Status); await jobClient.SetCommandContinueAsync(new List <string> { jobID }); await jobServer.ContinueJobsAsync(); //continue paused job Thread.Sleep(3000); job = await jobClient.GetJobAsync(jobID); await jobClient.SetCommandStopAsync(new List <string> { jobID }); await jobServer.StopJobsAsync(); Thread.Sleep(3000); await jobClient.DeleteJobsAsync(new List <string>() { jobID }); Assert.Equal(JobStatus.Running, job.Status); }
private static async Task SynchronizeAsync() { // Database script used for this sample : https://github.com/Mimetis/Dotmim.Sync/blob/master/CreateAdventureWorks.sql var serverOrchestrator = new WebRemoteOrchestrator("https://localhost:44342/api/sync"); // Second provider is using plain old Sql Server provider, relying on triggers and tracking tables to create the sync environment var clientProvider = new SqlSyncProvider(clientConnectionString); // Set the web server Options var options = new SyncOptions { BatchDirectory = Path.Combine(SyncOptions.GetDefaultUserBatchDiretory(), "client") }; // Creating an agent that will handle all the process var agent = new SyncAgent(clientProvider, serverOrchestrator, options); var progress = new SynchronousProgress <ProgressArgs>( pa => Console.WriteLine($"{pa.ProgressPercentage:p}\t {pa.Message}")); var parameters = new SyncParameters { { "City", "Toronto" }, // Because I've specified that "postal" could be null, // I can set the value to DBNull.Value (and the get all postal code in Toronto city) { "postal", DBNull.Value } }; do { try { // Launch the sync process var s1 = await agent.SynchronizeAsync(progress); // Write results Console.WriteLine(s1); } catch (Exception ex) { Console.WriteLine(ex.Message); } } while (Console.ReadKey().Key != ConsoleKey.Escape); Console.WriteLine("End"); }
public void test(ResolverType resolverType) { // Arrange var initialized = false; var sum = 0; var completed = false; var task = new TaskDefinition( new BasicTaskStep( "Initialize", () => { initialized = true; }), PipelineTaskStep .Builder <int>("Pipeline") .WithInput(new[] { 1, 2, 3, 4, 5, 6 }) .WithBlock("Sum", x => sum += x) .WithBlock("Log", x => _output.WriteLine(x.ToString())) .Build(), new BasicTaskStep( "Complete", () => { completed = true; })); var progress = new SynchronousProgress <TaskProgress>(x => _output.WriteLine($"{x.StepName}: {x.ProgressPercentage}%")); var cancellationSource = new CancellationTokenSource(); var taskEvents = new TaskExecutionEvents( taskStarted: x => _output.WriteLine("Task started."), taskEnded: x => _output.WriteLine($"Task ended after {x.Duration.Ticks} ticks."), stepStarted: x => _output.WriteLine($"Step '{x.Step.Name}' started."), stepEnded: x => _output.WriteLine($"Step '{x.Step.Name}' ended after {x.Duration.Ticks} ticks.")); var pipelineEvents = new PipelineExecutionEvents( itemStarted: x => _output.WriteLine($"Item {x.ItemNumber} of step '{x.Step.Name}' started."), itemEnded: x => _output.WriteLine($"Item {x.ItemNumber} of step '{x.Step.Name}' ended after {x.Duration.Ticks} ticks."), blockStarted: x => _output.WriteLine($"Block '{x.Block.Name}' of step '{x.Step.Name}' started processing item {x.ItemNumber}."), blockEnded: x => _output.WriteLine($"Block '{x.Block.Name}' of step '{x.Step.Name}' ended processing item {x.ItemNumber} after {x.Duration.Ticks} ticks.")); // Act var result = task.Execute(resolverType, progress, cancellationSource, taskEvents, pipelineEvents); // Assert result.Outcome.Should().Be(TaskOutcome.Successful); initialized.Should().Be(true); sum.Should().Be(21); completed.Should().Be(true); }
private static async Task UpgradeAsync() { var serverProvider = new SqlSyncProvider(serverConnectionString); var remoteOrchestrator = new RemoteOrchestrator(serverProvider); do { var progress = new SynchronousProgress <ProgressArgs>(args => Console.WriteLine($"{args.ProgressPercentage:p}:\t{args.Message}")); await remoteOrchestrator.UpgradeAsync(progress : progress); // Write results Console.WriteLine("Upgrade to last version done"); } while (Console.ReadKey().Key != ConsoleKey.Escape); Console.WriteLine("End"); }
private void test( ResolverType resolverType, ITaskStep taskStep, ICollection <byte> expectedProgressReports) { // Arrange var progressReports = new List <TaskProgress>(); var task = new TaskDefinition(taskStep); var progress = new SynchronousProgress <TaskProgress>(x => progressReports.Add(x)); // Act task.Execute(resolverType, progress); // Assert progressReports.Select(x => x.StepName).Should().OnlyContain(x => x == taskStep.Name); progressReports.Select(x => x.ProgressPercentage).Should().BeEquivalentTo(expectedProgressReports); }
private void ExecuteStep <TStep>( TStep step, TaskDefinition task, IProgress <TaskProgress> progress, CancellationToken cancellation) where TStep : ITaskStep { var stepExecutor = _taskStepExecutorResolver.Resolve <TStep>(); var context = new TaskStepExecutionContext { Task = task, EventsBag = _executionEventsBag }; var stepProgress = new SynchronousProgress <byte>( x => progress.Report(new TaskProgress { StepName = step.Name, ProgressPercentage = x })); stepExecutor.Execute(step, context, stepProgress, cancellation); }
private static async Task SynchronizeThenDeprovisionThenProvisionAsync() { // Create 2 Sql Sync providers var serverProvider = new SqlSyncProvider(DBHelper.GetDatabaseConnectionString(serverDbName)); var clientProvider = new SqlSyncProvider(DBHelper.GetDatabaseConnectionString(clientDbName)); // Create standard Setup and Options var setup = new SyncSetup(new string[] { "Address", "Customer", "CustomerAddress" }); var options = new SyncOptions(); // Creating an agent that will handle all the process var agent = new SyncAgent(clientProvider, serverProvider, options, setup); // Using the Progress pattern to handle progession during the synchronization var progress = new SynchronousProgress <ProgressArgs>(s => Console.WriteLine($"{s.Context.SyncStage}:\t{s.Message}")); // First sync to have a starting point var s1 = await agent.SynchronizeAsync(progress); Console.WriteLine(s1); // ----------------------------------------------------------------- // Migrating a table by adding a new column // ----------------------------------------------------------------- // Adding a new column called CreatedDate to Address table, on the server, and on the client. await AddNewColumnToAddressAsync(serverProvider.CreateConnection()); await AddNewColumnToAddressAsync(clientProvider.CreateConnection()); // ----------------------------------------------------------------- // Server side // ----------------------------------------------------------------- // Creating a setup regarding only the table Address var setupAddress = new SyncSetup(new string[] { "Address" }); // Create a server orchestrator used to Deprovision and Provision only table Address var remoteOrchestrator = new RemoteOrchestrator(serverProvider, options, setupAddress); // Unprovision the Address triggers / stored proc. // We can conserve the Address tracking table, since we just add a column, // that is not a primary key used in the tracking table // That way, we are preserving historical data await remoteOrchestrator.DeprovisionAsync(SyncProvision.StoredProcedures | SyncProvision.Triggers); // Provision the Address triggers / stored proc again, // This provision method will fetch the address schema from the database, // so it will contains all the columns, including the new Address column added await remoteOrchestrator.ProvisionAsync(SyncProvision.StoredProcedures | SyncProvision.Triggers); // Now we need the full setup to get the full schema. // Setup includes [Address] [Customer] and [CustomerAddress] remoteOrchestrator.Setup = setup; var newSchema = await remoteOrchestrator.GetSchemaAsync(); // Now we need to save this new schema to the serverscope table // get the server scope again var serverScope = await remoteOrchestrator.GetServerScopeAsync(); // affect good values serverScope.Setup = setup; serverScope.Schema = newSchema; // save it await remoteOrchestrator.WriteServerScopeAsync(serverScope); // ----------------------------------------------------------------- // Client side // ----------------------------------------------------------------- // Now go for local orchestrator var localOrchestrator = new LocalOrchestrator(clientProvider, options, setupAddress); // Unprovision the Address triggers / stored proc. We can conserve tracking table, since we just add a column, that is not a primary key used in the tracking table // In this case, we will await localOrchestrator.DeprovisionAsync(SyncProvision.StoredProcedures | SyncProvision.Triggers); // Provision the Address triggers / stored proc again, // This provision method will fetch the address schema from the database, so it will contains all the columns, including the new one added await localOrchestrator.ProvisionAsync(SyncProvision.StoredProcedures | SyncProvision.Triggers); // Now we need to save this to clientscope // get the server scope again var clientScope = await localOrchestrator.GetClientScopeAsync(); // At this point, if you need the schema and you are not able to create a RemoteOrchestrator, // You can create a WebClientOrchestrator and get the schema as well // var proxyClientProvider = new WebClientOrchestrator("https://localhost:44369/api/Sync"); // var newSchema = proxyClientProvider.GetSchemaAsync(); // affect good values clientScope.Setup = setup; clientScope.Schema = newSchema; // save it await localOrchestrator.WriteClientScopeAsync(clientScope); // Now test a new sync, everything should work as expected. do { // Console.Clear(); Console.WriteLine("Sync Start"); try { var s2 = await agent.SynchronizeAsync(); // Write results Console.WriteLine(s2); } catch (Exception e) { Console.WriteLine(e.Message); } } while (Console.ReadKey().Key != ConsoleKey.Escape); Console.WriteLine("End"); }
private static async Task TestDeleteWithoutBulkAsync() { var cs = DBHelper.GetDatabaseConnectionString(serverProductCategoryDbName); var cc = DBHelper.GetDatabaseConnectionString(clientDbName); // Create 2 Sql Sync providers var serverProvider = new SqlSyncProvider(cs); var serverConnection = new SqlConnection(cs); //var clientProvider = new SqlSyncProvider(cc); //var clientConnection = new SqlConnection(cc); var clientProvider = new SqliteSyncProvider("advworks2.db"); var clientConnection = new SqliteConnection(clientProvider.ConnectionString); // Creating an agent that will handle all the process var agent = new SyncAgent(clientProvider, serverProvider, new string[] { "ProductCategory" }); // Using the Progress pattern to handle progession during the synchronization var progress = new SynchronousProgress <ProgressArgs>(s => { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine($"{s.Context.SyncStage}:\t{s.Message}"); Console.ResetColor(); }); var remoteProgress = new SynchronousProgress <ProgressArgs>(s => { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine($"{s.Context.SyncStage}:\t{s.Message}"); Console.ResetColor(); }); // agent.AddRemoteProgress(remoteProgress); agent.Options.BatchDirectory = Path.Combine(SyncOptions.GetDefaultUserBatchDiretory(), "sync"); agent.Options.BatchSize = 0; agent.Options.UseBulkOperations = false; agent.Options.DisableConstraintsOnApplyChanges = false; agent.Options.ConflictResolutionPolicy = ConflictResolutionPolicy.ClientWins; Console.WriteLine("Sync Start"); var s1 = await agent.SynchronizeAsync(progress); Console.WriteLine(s1); Console.WriteLine("Insert product category on Server"); var id = InsertOneProductCategoryId(serverConnection, "GLASSES"); Console.WriteLine("Update Done."); Console.WriteLine("Update product category on Server"); UpdateOneProductCategoryId(serverConnection, id, "OVERGLASSES"); Console.WriteLine("Update Done."); Console.WriteLine("Sync Start"); s1 = await agent.SynchronizeAsync(progress); Console.WriteLine(s1); Console.WriteLine("End"); }
/// <summary> /// Test a client syncing through a web api /// </summary> private static async Task SyncThroughWebApiAsync() { var clientProvider = new SqlSyncProvider(DbHelper.GetDatabaseConnectionString(clientDbName)); var handler = new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip }; var client = new HttpClient(handler) { Timeout = TimeSpan.FromMinutes(5) }; var proxyClientProvider = new WebClientOrchestrator("http://localhost:52288/api/Sync", null, null, client); // ---------------------------------- // Client side // ---------------------------------- var clientOptions = new SyncOptions { ScopeInfoTableName = "client_scopeinfo", BatchDirectory = Path.Combine(SyncOptions.GetDefaultUserBatchDiretory(), "sync_client"), BatchSize = 50, CleanMetadatas = true, UseBulkOperations = true, UseVerboseErrors = false, }; var clientSetup = new SyncSetup { StoredProceduresPrefix = "cli", StoredProceduresSuffix = "", TrackingTablesPrefix = "cli", TrackingTablesSuffix = "", TriggersPrefix = "", TriggersSuffix = "", }; var agent = new SyncAgent(clientProvider, proxyClientProvider, clientSetup, clientOptions); Console.WriteLine("Press a key to start (be sure web api is running ...)"); Console.ReadKey(); do { Console.Clear(); Console.WriteLine("Web sync start"); try { var progress = new SynchronousProgress <ProgressArgs>(pa => Console.WriteLine($"{pa.Context.SessionId} - {pa.Context.SyncStage}\t {pa.Message}")); var s = await agent.SynchronizeAsync(progress); Console.WriteLine(s); } catch (SyncException e) { Console.WriteLine(e.Message); } catch (Exception e) { Console.WriteLine("UNKNOW EXCEPTION : " + e.Message); } Console.WriteLine("Sync Ended. Press a key to start again, or Escapte to end"); } while (Console.ReadKey().Key != ConsoleKey.Escape); Console.WriteLine("End"); }
private static async Task SynchronizeAsync(CoreProvider clientProvider) { // Database script used for this sample : https://github.com/Mimetis/Dotmim.Sync/blob/master/CreateAdventureWorks.sql var serverOrchestrator = new WebClientOrchestrator("https://localhost:44342/api/sync"); // Creating an agent that will handle all the process var agent = new SyncAgent(clientProvider, serverOrchestrator); try { var progress = new SynchronousProgress <ProgressArgs>(args => Console.WriteLine($"{args.PogressPercentageString}:\t{args.Message}")); // Get the client scope var scope = await agent.LocalOrchestrator.GetClientScopeAsync(); if (scope.IsNewScope) { var client = new HttpClient(); var seedingsResponse = await client.GetAsync($"https://localhost:44342/api/sync/seedings/{scope.Id}"); seedingsResponse.EnsureSuccessStatusCode(); var seedingsResponseString = await seedingsResponse.Content.ReadAsStringAsync(); var seedings = JsonConvert.DeserializeObject <List <Seeding> >(seedingsResponseString); agent.LocalOrchestrator.OnTableCreating(async tca => { var tableName = tca.TableName.Unquoted().ToString(); var schemaName = string.IsNullOrEmpty(tca.TableName.SchemaName) ? "dbo" : tca.TableName.SchemaName; var seeding = seedings.FirstOrDefault(s => s.TableName == tableName && s.SchemaName == schemaName); var id = tca.Table.GetPrimaryKeysColumns().ToList()[0]; if (seeding != null && id.IsAutoIncrement) { id.AutoIncrementSeed = seeding.Seed; id.AutoIncrementStep = seeding.Step; } var newTableBuilder = agent.LocalOrchestrator.GetTableBuilder(tca.Table, agent.LocalOrchestrator.Setup); var newCommand = await newTableBuilder.GetCreateTableCommandAsync(tca.Connection, tca.Transaction); tca.Command.CommandText = newCommand.CommandText; }); } // Launch the sync process var s1 = await agent.SynchronizeAsync(progress); // Write results Console.WriteLine(s1); } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.WriteLine("End"); }
private static async Task SynchronizeAsync() { // Database script used for this sample : https://github.com/Mimetis/Dotmim.Sync/blob/master/CreateAdventureWorks.sql // Create 2 Sql Sync providers // First provider is using the Sql change tracking feature. Don't forget to enable it on your database until running this code ! // For instance, use this SQL statement on your server database : ALTER DATABASE AdventureWorks SET CHANGE_TRACKING = ON (CHANGE_RETENTION = 10 DAYS, AUTO_CLEANUP = ON) // Otherwise, if you don't want to use Change Tracking feature, just change 'SqlSyncChangeTrackingProvider' to 'SqlSyncProvider' var serverProvider = new SqlSyncChangeTrackingProvider(serverConnectionString); var clientProvider = new SqlSyncProvider(clientConnectionString); // Tables involved in the sync process: var tables = new string[] { "ProductCategory", "ProductModel", "Product", "Address", "Customer", "CustomerAddress", "SalesOrderHeader", "SalesOrderDetail" }; // Creating an agent that will handle all the process var agent = new SyncAgent(clientProvider, serverProvider, tables); agent.Options.BatchSize = 20; // Using the IProgress<T> pattern to handle progession dring the synchronization // Be careful, Progress<T> is not synchronous. Use SynchronousProgress<T> instead ! var progress = new SynchronousProgress <ProgressArgs>(args => Console.WriteLine($"{args.PogressPercentageString}:\t{args.Message}")); // -------------------------------------------- // Using Interceptors // -------------------------------------------- // CancellationTokenSource is used to cancel a sync process in the next example var cts = new CancellationTokenSource(); // Intercept a table changes selecting // Because the changes are not yet selected, we can easily interrupt the process with the cancellation token agent.LocalOrchestrator.OnTableChangesSelecting(args => { Console.WriteLine($"-------- Getting changes from table {args.Table.GetFullName()} ..."); if (args.Table.TableName == "Table_That_Should_Not_Be_Sync") { cts.Cancel(); } }); // Intercept a table changes applying with a particular state [Upsert] or [Deleted] // The rows included in the args.Changes table will be applied right after. agent.LocalOrchestrator.OnTableChangesBatchApplying(args => { Console.WriteLine($"-------- Applying changes {args.State} to table {args.Changes.GetFullName()} ..."); if (args.Changes == null || args.Changes.Rows.Count == 0) { return; } foreach (var row in args.Changes.Rows) { Console.WriteLine(row); } }); // Intercept a table changes selected. // The rows included in the args.Changes have been selected from the datasource and will be sent to the server agent.LocalOrchestrator.OnTableChangesSelected(args => { if (args.Changes == null || args.Changes.Rows.Count == 0) { return; } foreach (var row in args.Changes.Rows) { Console.WriteLine(row); } }); // ------------------------ // Because we are in a TCP mode, we can hook the server side events // In an HTTP mode, the code below won't work // ------------------------ agent.RemoteOrchestrator.OnTableChangesSelecting(args => { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine($"-------- Getting changes from table {args.Table.GetFullName()} ..."); Console.ResetColor(); }); agent.RemoteOrchestrator.OnTableChangesSelected(args => { if (args.Changes == null || args.Changes.Rows.Count == 0) { return; } Console.ForegroundColor = ConsoleColor.Yellow; foreach (var row in args.Changes.Rows) { Console.WriteLine(row); } Console.ResetColor(); }); agent.RemoteOrchestrator.OnTableChangesBatchApplying(args => { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine($"-------- Applying changes {args.State} to table {args.Changes.GetFullName()} ..."); if (args.Changes == null || args.Changes.Rows.Count == 0) { return; } foreach (var row in args.Changes.Rows) { Console.WriteLine(row); } Console.ResetColor(); }); do { // Launch the sync process var s1 = await agent.SynchronizeAsync(SyncType.Normal, cts.Token, progress); // Write results Console.WriteLine(s1); } while (Console.ReadKey().Key != ConsoleKey.Escape); Console.WriteLine("End"); }
private static async Task CreateSnapshotAsync() { // Create 2 Sql Sync providers var serverProvider = new SqlSyncProvider(DbHelper.GetDatabaseConnectionString(serverDbName)); var remoteOrchestrator = new RemoteOrchestrator(serverProvider); // specific Setup with only 2 tables, and one filtered var setup = new SyncSetup(allTables); // snapshot directory var directory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Snapshots"); await remoteOrchestrator.CreateSnapshotAsync(new SyncContext(), setup, directory, 500, CancellationToken.None); // client provider var clientProvider = new SqlSyncProvider(DbHelper.GetDatabaseConnectionString(clientDbName)); // Insert a value after snapshot created using (var c = serverProvider.CreateConnection()) { var command = c.CreateCommand(); command.CommandText = "INSERT INTO [dbo].[ProductCategory] ([Name]) VALUES ('Bikes revolution');"; c.Open(); command.ExecuteNonQuery(); c.Close(); } // Creating an agent that will handle all the process var agent = new SyncAgent(clientProvider, serverProvider, setup); var syncOptions = new SyncOptions { SnapshotsDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Snapshots") }; // Setting the snapshots directory for client agent.Options.SnapshotsDirectory = directory; // Using the Progress pattern to handle progession during the synchronization var progress = new SynchronousProgress <ProgressArgs>(s => { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine($"{s.Context.SyncStage}:\t{s.Message}"); Console.ResetColor(); }); var remoteProgress = new SynchronousProgress <ProgressArgs>(s => { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine($"{s.Context.SyncStage}:\t{s.Message}"); Console.ResetColor(); }); //// Launch the sync process //if (!agent.Parameters.Contains("City")) // agent.Parameters.Add("City", "Toronto"); //if (!agent.Parameters.Contains("postal")) // agent.Parameters.Add("postal", "NULL"); do { Console.Clear(); Console.WriteLine("Sync Start"); try { var s1 = await agent.SynchronizeAsync(progress); Console.WriteLine(s1); } catch (Exception e) { Console.WriteLine(e.Message); } } while (Console.ReadKey().Key != ConsoleKey.Escape); Console.WriteLine("End"); }
private static async Task SynchronizeMultiScopesAsync() { // Create 2 Sql Sync providers var serverProvider = new SqlSyncChangeTrackingProvider(DbHelper.GetDatabaseConnectionString(serverDbName)); var clientProvider = new SqlSyncProvider(DbHelper.GetDatabaseConnectionString(clientDbName)); // Create 2 tables list (one for each scope) string[] productScopeTables = new string[] { "ProductCategory", "ProductModel", "Product" }; string[] customersScopeTables = new string[] { "Address", "Customer", "CustomerAddress", "SalesOrderHeader", "SalesOrderDetail" }; // Create 2 sync setup with named scope var setupProducts = new SyncSetup(productScopeTables, "productScope"); var setupCustomers = new SyncSetup(customersScopeTables, "customerScope"); // Create 2 agents, one for each scope var agentProducts = new SyncAgent(clientProvider, serverProvider, setupProducts); var agentCustomers = new SyncAgent(clientProvider, serverProvider, setupCustomers); // Using the Progress pattern to handle progession during the synchronization // We can use the same progress for each agent var progress = new SynchronousProgress <ProgressArgs>(s => { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine($"{s.Context.SyncStage}:\t{s.Message}"); Console.ResetColor(); }); var remoteProgress = new SynchronousProgress <ProgressArgs>(s => { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine($"{s.Context.SyncStage}:\t{s.Message}"); Console.ResetColor(); }); // Spying what's going on the server side agentProducts.AddRemoteProgress(remoteProgress); agentCustomers.AddRemoteProgress(remoteProgress); do { Console.Clear(); Console.WriteLine("Sync Start"); try { Console.WriteLine("Hit 1 for sync Products. Hit 2 for sync customers and sales"); var k = Console.ReadKey().Key; if (k == ConsoleKey.D1) { Console.WriteLine("Sync Products:"); var s1 = await agentProducts.SynchronizeAsync(progress); Console.WriteLine(s1); } else { Console.WriteLine("Sync Customers and Sales:"); var s1 = await agentCustomers.SynchronizeAsync(progress); Console.WriteLine(s1); } } catch (Exception e) { Console.WriteLine(e.Message); } } while (Console.ReadKey().Key != ConsoleKey.Escape); Console.WriteLine("End"); }
/// <summary> /// Launch a simple sync, over TCP network, each sql server (client and server are reachable through TCP cp /// </summary> /// <returns></returns> private static async Task SynchronizeAsync() { // Create 2 Sql Sync providers var serverProvider = new SqlSyncProvider(DbHelper.GetDatabaseConnectionString(serverDbName)); //var clientProvider = new SqlSyncProvider(DbHelper.GetDatabaseConnectionString(clientDbName)); var clientProvider = new SqliteSyncProvider("clientX.db"); //var setup = new SyncSetup(new string[] { "Address", "Customer", "CustomerAddress", "SalesOrderHeader", "SalesOrderDetail" }); var setup = new SyncSetup(allTables); setup.Filters.Add("Customer", "CompanyName"); var addressCustomerFilter = new SetupFilter("CustomerAddress"); addressCustomerFilter.AddParameter("CompanyName", "Customer"); addressCustomerFilter.AddJoin(Join.Left, "Customer").On("CustomerAddress", "CustomerId", "Customer", "CustomerId"); addressCustomerFilter.AddWhere("CompanyName", "Customer", "CompanyName"); setup.Filters.Add(addressCustomerFilter); var addressFilter = new SetupFilter("Address"); addressFilter.AddParameter("CompanyName", "Customer"); addressFilter.AddJoin(Join.Left, "CustomerAddress").On("CustomerAddress", "AddressId", "Address", "AddressId"); addressFilter.AddJoin(Join.Left, "Customer").On("CustomerAddress", "CustomerId", "Customer", "CustomerId"); addressFilter.AddWhere("CompanyName", "Customer", "CompanyName"); setup.Filters.Add(addressFilter); var orderHeaderFilter = new SetupFilter("SalesOrderHeader"); orderHeaderFilter.AddParameter("CompanyName", "Customer"); orderHeaderFilter.AddJoin(Join.Left, "CustomerAddress").On("CustomerAddress", "CustomerId", "SalesOrderHeader", "CustomerId"); orderHeaderFilter.AddJoin(Join.Left, "Customer").On("CustomerAddress", "CustomerId", "Customer", "CustomerId"); orderHeaderFilter.AddWhere("CompanyName", "Customer", "CompanyName"); setup.Filters.Add(orderHeaderFilter); var orderDetailsFilter = new SetupFilter("SalesOrderDetail"); orderDetailsFilter.AddParameter("CompanyName", "Customer"); orderDetailsFilter.AddJoin(Join.Left, "SalesOrderHeader").On("SalesOrderDetail", "SalesOrderID", "SalesOrderHeader", "SalesOrderID"); orderDetailsFilter.AddJoin(Join.Left, "CustomerAddress").On("CustomerAddress", "CustomerId", "SalesOrderHeader", "CustomerId"); orderDetailsFilter.AddJoin(Join.Left, "Customer").On("CustomerAddress", "CustomerId", "Customer", "CustomerId"); orderDetailsFilter.AddWhere("CompanyName", "Customer", "CompanyName"); setup.Filters.Add(orderDetailsFilter); // Add pref suf setup.StoredProceduresPrefix = "s"; setup.StoredProceduresSuffix = ""; setup.TrackingTablesPrefix = "t"; setup.TrackingTablesSuffix = ""; // Creating an agent that will handle all the process var agent = new SyncAgent(clientProvider, serverProvider, setup); // Using the Progress pattern to handle progession during the synchronization var progress = new SynchronousProgress <ProgressArgs>(s => { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine($"{s.Context.SyncStage}:\t{s.Message}"); Console.ResetColor(); }); var remoteProgress = new SynchronousProgress <ProgressArgs>(s => { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine($"{s.Context.SyncStage}:\t{s.Message}"); Console.ResetColor(); }); agent.AddRemoteProgress(remoteProgress); //agent.Options.BatchDirectory = Path.Combine(SyncOptions.GetDefaultUserBatchDiretory(), "sync"); agent.Options.BatchSize = 1000; agent.Options.CleanMetadatas = true; agent.Options.UseBulkOperations = false; agent.Options.DisableConstraintsOnApplyChanges = false; agent.Options.ConflictResolutionPolicy = ConflictResolutionPolicy.ClientWins; //agent.Options.UseVerboseErrors = false; //agent.Options.ScopeInfoTableName = "tscopeinfo"; var myRijndael = new RijndaelManaged(); myRijndael.GenerateKey(); myRijndael.GenerateIV(); //agent.RemoteOrchestrator.OnSerializingSet(ssa => //{ // // Create an encryptor to perform the stream transform. // var encryptor = myRijndael.CreateEncryptor(myRijndael.Key, myRijndael.IV); // using (var msEncrypt = new MemoryStream()) // { // using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) // { // using (var swEncrypt = new StreamWriter(csEncrypt)) // { // //Write all data to the stream. // var strSet = JsonConvert.SerializeObject(ssa.Set); // swEncrypt.Write(strSet); // } // ssa.Data = msEncrypt.ToArray(); // } // } //}); //agent.OnApplyChangesFailed(acf => //{ // // Check conflict is correctly set // var localRow = acf.Conflict.LocalRow; // var remoteRow = acf.Conflict.RemoteRow; // // Merge row // acf.Resolution = ConflictResolution.MergeRow; // acf.FinalRow["Name"] = "Prout"; //}); do { Console.Clear(); Console.WriteLine("Sync Start"); try { // Launch the sync process if (!agent.Parameters.Contains("CompanyName")) { agent.Parameters.Add("CompanyName", "Professional Sales and Service"); } //if (!agent.Parameters.Contains("postal")) // agent.Parameters.Add("postal", DBNull.Value); var s1 = await agent.SynchronizeAsync(progress); // Write results Console.WriteLine(s1); } catch (Exception e) { Console.WriteLine(e.Message); } //Console.WriteLine("Sync Ended. Press a key to start again, or Escapte to end"); } while (Console.ReadKey().Key != ConsoleKey.Escape); Console.WriteLine("End"); }
private static async Task SynchronizeThenDeprovisionThenProvisionAsync() { // Create 2 Sql Sync providers var serverProvider = new SqlSyncProvider(serverConnectionString); var clientProvider = new SqlSyncProvider(clientConnectionString); // Create standard Setup and Options var setup = new SyncSetup(new string[] { "Address", "Customer", "CustomerAddress" }); var options = new SyncOptions(); // Creating an agent that will handle all the process var agent = new SyncAgent(clientProvider, serverProvider, options, setup); // Using the Progress pattern to handle progession during the synchronization var progress = new SynchronousProgress <ProgressArgs>(args => Console.WriteLine($"{args.ProgressPercentage:p}:\t{args.Message}")); // First sync to have a starting point var s1 = await agent.SynchronizeAsync(progress); Console.WriteLine(s1); // ----------------------------------------------------------------- // Migrating a table by adding a new column // ----------------------------------------------------------------- // Adding a new column called CreatedDate to Address table, on the server, and on the client. await Helper.AddNewColumnToAddressAsync(serverProvider.CreateConnection()); await Helper.AddNewColumnToAddressAsync(clientProvider.CreateConnection()); // ----------------------------------------------------------------- // Server side // ----------------------------------------------------------------- // Creating a setup regarding only the table Address var setupAddress = new SyncSetup(new string[] { "Address" }); // Create a server orchestrator used to Deprovision and Provision only table Address var remoteOrchestrator = new RemoteOrchestrator(serverProvider, options, setupAddress); // Unprovision the old Address triggers / stored proc. // We can conserve the Address tracking table, since we just add a column, // that is not a primary key used in the tracking table // That way, we are preserving historical data await remoteOrchestrator.DeprovisionAsync(SyncProvision.StoredProcedures | SyncProvision.Triggers); // Provision the new Address triggers / stored proc again, // This provision method will fetch the address schema from the database, // so it will contains all the columns, including the new Address column added await remoteOrchestrator.ProvisionAsync(SyncProvision.StoredProcedures | SyncProvision.Triggers); // ----------------------------------------------------------------- // Client side // ----------------------------------------------------------------- // Now go for local orchestrator var localOrchestrator = new LocalOrchestrator(clientProvider, options, setupAddress); // Unprovision the Address triggers / stored proc. We can conserve tracking table, since we just add a column, that is not a primary key used in the tracking table // In this case, we will await localOrchestrator.DeprovisionAsync(SyncProvision.StoredProcedures | SyncProvision.Triggers); // Provision the Address triggers / stored proc again, // This provision method will fetch the address schema from the database, so it will contains all the columns, including the new one added await localOrchestrator.ProvisionAsync(SyncProvision.StoredProcedures | SyncProvision.Triggers); // Now test a new sync, everything should work as expected. do { // Console.Clear(); Console.WriteLine("Sync Start"); try { var s2 = await agent.SynchronizeAsync(); // Write results Console.WriteLine(s2); } catch (Exception e) { Console.WriteLine(e.Message); } } while (Console.ReadKey().Key != ConsoleKey.Escape); Console.WriteLine("End"); }
private static async Task MigrateClientsUsingMultiScopesAsync() { // Create the server Sync provider var serverProvider = new SqlSyncProvider(serverConnectionString); // Create 2 clients. First will migrate, 2nd will stay without new column var client1Provider = new SqlSyncProvider(clientConnectionString); var databaseName = $"{Path.GetRandomFileName().Replace(".", "").ToLowerInvariant()}.db"; var client2Provider = new SqliteSyncProvider(databaseName); // Create standard Setup var setup = new SyncSetup("Address", "Customer", "CustomerAddress"); // Creating agents that will handle all the process var agent1 = new SyncAgent(client1Provider, serverProvider); var agent2 = new SyncAgent(client2Provider, serverProvider); // Using the Progress pattern to handle progession during the synchronization var progress = new SynchronousProgress <ProgressArgs>(args => Console.WriteLine($"{args.ProgressPercentage:p}:\t{args.Message}")); // First sync to have a starting point // To make a full example, we are going to use differente scope name (v0, v1) // v0 is the initial database // v1 will contains the new column in the Address table var s1 = await agent1.SynchronizeAsync("v0", setup, progress); Console.WriteLine("Initial Sync on Sql Server Client 1"); Console.WriteLine(s1); var s2 = await agent2.SynchronizeAsync("v0", setup, progress); Console.WriteLine("Initial Sync on Sqlite Client 2"); Console.WriteLine(s2); // ----------------------------------------------------------------- // Migrating a table by adding a new column // ----------------------------------------------------------------- // Adding a new column called CreatedDate to Address table, on the server await Helper.AddNewColumnToAddressAsync(new SqlConnection(serverConnectionString)); Console.WriteLine("Column added on server"); // ----------------------------------------------------------------- // Server side // ----------------------------------------------------------------- // Creating a new setup with the same tables // We are going to provision a new scope (v1) // Since this scope is not existing yet, it will force DMS to refresh the schema and // get the new column var setupAddress = new SyncSetup("Address", "Customer", "CustomerAddress"); // Create a server orchestrator used to Deprovision and Provision only table Address var remoteOrchestrator = new RemoteOrchestrator(serverProvider); // Provision everything again for this new scope v1, // This provision method will fetch the address schema from the database, // since the new scope name is not existing yet // so it will contains all the columns, including the new Address column added await remoteOrchestrator.ProvisionAsync("v1", setupAddress, progress : progress); Console.WriteLine("Server migration with new column CreatedDate done."); // At this point, server database has two scopes: // v0 : first scope with Address table without the new column // v1 : second scope with Address table with the new column CreatedDate // Take a look at the database in SQL management studio and see differences in stored proc // Now add a row on the server (with the new column) var addressId = await Helper.InsertOneAddressWithNewColumnAsync(new SqlConnection(serverConnectionString)); Console.WriteLine($"New address row added with pk {addressId}"); // ----------------------------------------------------------------- // SQlite Client will stay on old schema (without the new CreatedDate column) // ----------------------------------------------------------------- // First of all, we are still able to sync the local database without having to migrate the client // allows old clients that do not have the new column, to continue sync normally // these old clients will continue to sync on the v0 scope var s3 = await agent2.SynchronizeAsync("v0", setup, progress : progress); Console.WriteLine($"Sqlite not migrated, doing a sync on first scope v0:"); Console.WriteLine(s3); // If we get the row from the client, we have the new row inserted on server, // but without the new column var client2row = await Helper.GetLastAddressRowAsync(client2Provider.CreateConnection(), addressId); Console.WriteLine(client2row); // ----------------------------------------------------------------- // SQL Server Client will add the column and will sync on the new scope (with the new CreatedDate column) // ----------------------------------------------------------------- // Now we are going to upgrade the client 1 // adding the column to the client await Helper.AddNewColumnToAddressAsync(new SqlConnection(clientConnectionString)); Console.WriteLine("Sql Server client1 migration with new column CreatedDate done."); // Provision client with the new the V1 scope // Getting the scope from server and apply it locally var serverScope = await agent1.RemoteOrchestrator.GetServerScopeInfoAsync("v1", progress : progress); var v1clientScope = await agent1.LocalOrchestrator.ProvisionAsync(serverScope, progress : progress); Console.WriteLine("Sql Server client1 Provision done."); // if you look the stored procs on your local sql database // you will that you have the two scopes (v0 and v1) // TRICKY PART /* * The scope v1 is new. * If we sync now, since v1 is new, we are going to sync all the rows from start * What we want is to sync from the last point we sync the old v0 scope * That's why we are shadowing the metadata info from v0 into v1 */ var v0clientScope = await agent1.LocalOrchestrator.GetClientScopeInfoAsync("v0"); v1clientScope.ShadowScope(v0clientScope); v1clientScope = await agent1.LocalOrchestrator.SaveClientScopeInfoAsync(v1clientScope); // Now test a new sync, on this new scope v1 var s4 = await agent1.SynchronizeAsync("v1", progress : progress); Console.WriteLine($"Sql Server client1 migrated, doing a sync on second scope v1:"); Console.WriteLine(s4); // If we get the client row from the client database, it should contains the value var client1row = await Helper.GetLastAddressRowAsync(new SqlConnection(clientConnectionString), addressId); Console.WriteLine(client1row); // OPTIONAL // ----------------------------------------------------------------- // On this new client, migrated, we no longer need the v0 scope // we can deprovision it await agent1.LocalOrchestrator.DeprovisionAsync("v0", SyncProvision.StoredProcedures, progress : progress); await agent1.LocalOrchestrator.DeleteClientScopeInfoAsync(v0clientScope, progress : progress); Console.WriteLine($"Deprovision of old scope v0 done on Sql Server client1"); // ----------------------------------------------------------------- // SQLite Client will eventually migrate to v1 // ----------------------------------------------------------------- // It's time to migrate the sqlite client // Adding the column to the SQLite client await Helper.AddNewColumnToAddressAsync(client2Provider.CreateConnection()); Console.WriteLine($"Column eventually added to Sqlite client2"); // Provision SQLite client with the new the V1 scope var v1client2Scope = await agent2.LocalOrchestrator.ProvisionAsync(serverScope, progress : progress); Console.WriteLine($"Provision v1 done on SQLite client2"); // ShadowScope old scope to new scope var v0client2Scope = await agent2.LocalOrchestrator.GetClientScopeInfoAsync("v0"); v1client2Scope.ShadowScope(v0client2Scope); v1client2Scope = await agent2.LocalOrchestrator.SaveClientScopeInfoAsync(v1client2Scope); // let's try to sync firstly // Now test a new sync, on this new scope v1 // Obviously, we don't have anything from the server var s5 = await agent2.SynchronizeAsync("v1", progress : progress); Console.WriteLine(s5); // If we get the row from client, we have the new column, but value remains null // since this row was synced before client migration client2row = await Helper.GetLastAddressRowAsync(client2Provider.CreateConnection(), addressId); Console.WriteLine(client2row); // What we can do here, is just make a sync with Renit var s6 = await agent2.SynchronizeAsync("v1", SyncType.ReinitializeWithUpload, progress : progress); Console.WriteLine($"Making a full Reinitialize sync on SQLite client2"); Console.WriteLine(s6); // And now the row is correct // If we get the row from client, we have the new column, but value remains null // since this row was synced before client migration client2row = await Helper.GetLastAddressRowAsync(client2Provider.CreateConnection(), addressId); Console.WriteLine(client2row); // Migration done Console.WriteLine("End"); }
public static async Task SyncHttpThroughKestellAsync() { // server provider // Create 2 Sql Sync providers var serverProvider = new SqlSyncProvider(DbHelper.GetDatabaseConnectionString(serverDbName)); var clientProvider = new SqlSyncProvider(DbHelper.GetDatabaseConnectionString(clientDbName)); // ---------------------------------- // Client side // ---------------------------------- var clientOptions = new SyncOptions { BatchSize = 500 }; var proxyClientProvider = new WebClientOrchestrator(); // ---------------------------------- // Web Server side // ---------------------------------- var setup = new SyncSetup(allTables) { ScopeName = "all_tables_scope", StoredProceduresPrefix = "s", StoredProceduresSuffix = "", TrackingTablesPrefix = "t", TrackingTablesSuffix = "", TriggersPrefix = "", TriggersSuffix = "t" }; // snapshot directory var directory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Snapshots"); // ---------------------------------- // Create a snapshot // ---------------------------------- Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine($"Creating snapshot"); var remoteOrchestrator = new RemoteOrchestrator(serverProvider); await remoteOrchestrator.CreateSnapshotAsync(new SyncContext(), setup, directory, 500, CancellationToken.None); Console.WriteLine($"Done."); Console.ResetColor(); // ---------------------------------- // Insert a value after snapshot created // ---------------------------------- using (var c = serverProvider.CreateConnection()) { var command = c.CreateCommand(); command.CommandText = "INSERT INTO [dbo].[ProductCategory] ([Name]) VALUES ('Bikes revolution');"; c.Open(); command.ExecuteNonQuery(); c.Close(); } var webServerOptions = new WebServerOptions { SnapshotsDirectory = directory }; // Creating an agent that will handle all the process var agent = new SyncAgent(clientProvider, proxyClientProvider); agent.Options = clientOptions; var configureServices = new Action <IServiceCollection>(services => { services.AddSyncServer <SqlSyncProvider>(serverProvider.ConnectionString, setup, webServerOptions); }); var serverHandler = new RequestDelegate(async context => { var webProxyServer = context.RequestServices.GetService(typeof(WebProxyServerOrchestrator)) as WebProxyServerOrchestrator; await webProxyServer.HandleRequestAsync(context); }); using (var server = new KestrellTestServer(configureServices)) { var clientHandler = new ResponseDelegate(async(serviceUri) => { proxyClientProvider.ServiceUri = serviceUri; do { Console.Clear(); Console.WriteLine("Web sync start"); try { var progress = new SynchronousProgress <ProgressArgs>(pa => Console.WriteLine($"{pa.Context.SyncStage}\t {pa.Message}")); var s = await agent.SynchronizeAsync(progress); Console.WriteLine(s); } catch (SyncException e) { Console.WriteLine(e.ToString()); } catch (Exception e) { Console.WriteLine("UNKNOW EXCEPTION : " + e.Message); } Console.WriteLine("Sync Ended. Press a key to start again, or Escapte to end"); } while (Console.ReadKey().Key != ConsoleKey.Escape); }); await server.Run(serverHandler, clientHandler); } }