private static async Task DeprovisionClientManuallyAsync() { // Create client provider var clientProvider = new SqlSyncProvider(clientConnectionString); // Create standard Setup and Options var setup = new SyncSetup(new string[] { "Address", "Customer", "CustomerAddress" }); var options = new SyncOptions(); // Create a local orchestrator used to Deprovision everything var localOrchestrator = new LocalOrchestrator(clientProvider, options, setup); // Get the local scope var clientScope = await localOrchestrator.GetClientScopeAsync(); // Deprovision everything await localOrchestrator.DeprovisionAsync(SyncProvision.StoredProcedures | SyncProvision.Triggers | SyncProvision.TrackingTable | SyncProvision.Table); // affect good values clientScope.Setup = null; clientScope.Schema = null; // save the local scope await localOrchestrator.SaveClientScopeAsync(clientScope); }
public async Task LocalOrchestrator_CancellationToken_ShouldInterrupt_EnsureScope_OnConnectionOpened() { var dbName = HelperDatabase.GetRandomName("tcp_lo_"); await HelperDatabase.CreateDatabaseAsync(ProviderType.Sql, dbName, true); var cs = HelperDatabase.GetConnectionString(ProviderType.Sql, dbName); var sqlProvider = new SqlSyncProvider(cs); var ctx = new AdventureWorksContext((dbName, ProviderType.Sql, sqlProvider), true, false); await ctx.Database.EnsureCreatedAsync(); var options = new SyncOptions(); var setup = new SyncSetup(this.Tables); var localOrchestrator = new LocalOrchestrator(sqlProvider, options, setup); var cts = new CancellationTokenSource(); localOrchestrator.OnConnectionOpen(args => cts.Cancel()); var se = await Assert.ThrowsAsync <SyncException>(async() => await localOrchestrator.GetClientScopeAsync(cts.Token)); Assert.Equal(SyncSide.ClientSide, se.Side); Assert.Equal("OperationCanceledException", se.TypeName); HelperDatabase.DropDatabase(ProviderType.Sql, dbName); }
public async Task LocalOrchestrator_EnsureScope_CheckInterceptors() { var dbName = HelperDatabase.GetRandomName("tcp_lo_"); await HelperDatabase.CreateDatabaseAsync(ProviderType.Sql, dbName, true); var cs = HelperDatabase.GetConnectionString(ProviderType.Sql, dbName); var sqlProvider = new SqlSyncProvider(cs); var ctx = new AdventureWorksContext((dbName, ProviderType.Sql, sqlProvider), true, false); await ctx.Database.EnsureCreatedAsync(); var scopeName = "scope"; var options = new SyncOptions(); var setup = new SyncSetup(this.Tables); var onScopeLoading = false; var onScopeLoaded = false; var localOrchestrator = new LocalOrchestrator(sqlProvider, options, setup, scopeName); localOrchestrator.OnScopeLoading(args => { Assert.Equal(SyncStage.ScopeLoading, args.Context.SyncStage); Assert.Equal(scopeName, args.Context.ScopeName); Assert.Equal(scopeName, args.ScopeName); Assert.NotNull(args.Connection); Assert.NotNull(args.Transaction); Assert.Equal(ConnectionState.Open, args.Connection.State); Assert.Same(args.Connection, args.Transaction.Connection); onScopeLoading = true; }); localOrchestrator.OnScopeLoaded(args => { Assert.Equal(SyncStage.ScopeLoaded, args.Context.SyncStage); Assert.Equal(scopeName, args.Context.ScopeName); Assert.NotNull(args.ScopeInfo); Assert.Equal(scopeName, args.ScopeInfo.Name); Assert.NotNull(args.Connection); Assert.Null(args.Transaction); Assert.Equal(ConnectionState.Closed, args.Connection.State); onScopeLoaded = true; }); // Check connection and transaction interceptors BaseOrchestratorTests.AssertConnectionAndTransaction(localOrchestrator, SyncStage.ScopeLoading, SyncStage.ScopeLoaded); var localScopeInfo = await localOrchestrator.GetClientScopeAsync(); Assert.True(onScopeLoaded); Assert.True(onScopeLoading); HelperDatabase.DropDatabase(ProviderType.Sql, dbName); }
private static async Task ProvisionClientManuallyAsync() { // 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(); // ----------------------------------------------------------------- // Client side // ----------------------------------------------------------------- // This method is useful if you want to provision by yourself the client database // You will need to : // - Create a local orchestrator with the correct setup to provision // - Get the local scope that will contains after provisioning, the serialized version of your scope / schema // - Get the schema from the server side using a RemoteOrchestrator or a WebClientOrchestrator // - Provision everything locally // - Save the local scope information // Create a local orchestrator used to provision everything locally var localOrchestrator = new LocalOrchestrator(clientProvider, options, setup); // Because we need the schema from remote side, create a remote orchestrator var remoteOrchestrator = new RemoteOrchestrator(serverProvider, options, setup); // Getting the schema from server side var serverSchema = await remoteOrchestrator.GetSchemaAsync(); // 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 serverSchema = proxyClientProvider.GetSchemaAsync(); // get the local scope var clientScope = await localOrchestrator.GetClientScopeAsync(); // Provision everything needed (sp, triggers, tracking tables, AND TABLES) await localOrchestrator.ProvisionAsync(serverSchema, SyncProvision.StoredProcedures | SyncProvision.Triggers | SyncProvision.TrackingTable | SyncProvision.Table); // affect good values clientScope.Setup = setup; clientScope.Schema = serverSchema; // save the client scope await localOrchestrator.SaveClientScopeAsync(clientScope); }
public async Task LocalOrchestrator_EnsureScope_NewScope() { var dbName = HelperDatabase.GetRandomName("tcp_lo_"); await HelperDatabase.CreateDatabaseAsync(ProviderType.Sql, dbName, true); var cs = HelperDatabase.GetConnectionString(ProviderType.Sql, dbName); var sqlProvider = new SqlSyncProvider(cs); var ctx = new AdventureWorksContext((dbName, ProviderType.Sql, sqlProvider), true, false); await ctx.Database.EnsureCreatedAsync(); var scopeName = "scope"; var options = new SyncOptions(); var setup = new SyncSetup(this.Tables); var localOrchestrator = new LocalOrchestrator(sqlProvider, options, setup, scopeName); var localScopeInfo = await localOrchestrator.GetClientScopeAsync(); Assert.NotNull(localScopeInfo); Assert.Equal(scopeName, localScopeInfo.Name); Assert.True(localScopeInfo.IsNewScope); Assert.NotEqual(Guid.Empty, localScopeInfo.Id); Assert.Null(localScopeInfo.LastServerSyncTimestamp); Assert.Null(localScopeInfo.LastSync); Assert.Equal(0, localScopeInfo.LastSyncDuration); Assert.Null(localScopeInfo.LastSyncTimestamp); Assert.Null(localScopeInfo.Schema); Assert.Equal(SyncVersion.Current, new Version(localScopeInfo.Version)); // Check context SyncContext syncContext = localOrchestrator.GetContext(); Assert.Equal(scopeName, syncContext.ScopeName); Assert.NotEqual(Guid.Empty, syncContext.SessionId); Assert.Null(syncContext.Parameters); Assert.Equal(SyncStage.ScopeLoading, syncContext.SyncStage); Assert.Equal(SyncType.Normal, syncContext.SyncType); Assert.Equal(SyncWay.None, syncContext.SyncWay); HelperDatabase.DropDatabase(ProviderType.Sql, dbName); }
public async Task LocalOrchestrator_EnsureScope_ShouldNot_Fail_If_NoTables_In_Setup() { var dbName = HelperDatabase.GetRandomName("tcp_lo_"); await HelperDatabase.CreateDatabaseAsync(ProviderType.Sql, dbName, true); var cs = HelperDatabase.GetConnectionString(ProviderType.Sql, dbName); var options = new SyncOptions(); var setup = new SyncSetup(); var provider = new SqlSyncProvider(cs); var localOrchestrator = new LocalOrchestrator(provider, options, setup); var scope = await localOrchestrator.GetClientScopeAsync(); Assert.NotNull(scope); HelperDatabase.DropDatabase(ProviderType.Sql, dbName); }
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"); }
public async Task LocalOrchestrator_Scope() { var dbName = HelperDatabase.GetRandomName("tcp_lo_"); await HelperDatabase.CreateDatabaseAsync(ProviderType.Sql, dbName, true); var cs = HelperDatabase.GetConnectionString(ProviderType.Sql, dbName); var sqlProvider = new SqlSyncProvider(cs); var ctx = new AdventureWorksContext((dbName, ProviderType.Sql, sqlProvider), true, false); await ctx.Database.EnsureCreatedAsync(); var scopeName = "scope"; var options = new SyncOptions(); var setup = new SyncSetup(this.Tables); var localOrchestrator = new LocalOrchestrator(sqlProvider, options, setup, scopeName); var scopeTableCreating = 0; var scopeTableCreated = 0; var scopeLoading = 0; var scopeLoaded = 0; var scopeSaving = 0; var scopeSaved = 0; localOrchestrator.OnScopeSaving(ssa => { Assert.NotNull(ssa.Command); scopeSaving++; }); localOrchestrator.OnScopeSaved(ssa => scopeSaved++); localOrchestrator.OnScopeTableCreating(stca => { Assert.NotNull(stca.Command); scopeTableCreating++; }); localOrchestrator.OnScopeTableCreated(stca => { scopeTableCreated++; }); localOrchestrator.OnScopeLoading(args => { Assert.NotNull(args.Command); Assert.Equal(SyncStage.ScopeLoading, args.Context.SyncStage); Assert.Equal(scopeName, args.Context.ScopeName); Assert.Equal(scopeName, args.ScopeName); Assert.NotNull(args.Connection); Assert.NotNull(args.Transaction); Assert.Equal(ConnectionState.Open, args.Connection.State); Assert.Same(args.Connection, args.Transaction.Connection); scopeLoading++; }); localOrchestrator.OnScopeLoaded(args => { Assert.Equal(SyncStage.ScopeLoading, args.Context.SyncStage); Assert.Equal(scopeName, args.Context.ScopeName); Assert.NotNull(args.ScopeInfo); Assert.Equal(scopeName, args.ScopeInfo.Name); Assert.NotNull(args.Connection); Assert.NotNull(args.Transaction); scopeLoaded++; }); var localScopeInfo = await localOrchestrator.GetClientScopeAsync(); Assert.Equal(1, scopeTableCreating); Assert.Equal(1, scopeTableCreated); Assert.Equal(1, scopeLoading); Assert.Equal(1, scopeLoaded); Assert.Equal(1, scopeSaving); Assert.Equal(1, scopeSaved); scopeTableCreating = 0; scopeTableCreated = 0; scopeLoading = 0; scopeLoaded = 0; scopeSaving = 0; scopeSaved = 0; localScopeInfo.Version = "2.0"; await localOrchestrator.SaveClientScopeAsync(localScopeInfo); Assert.Equal(0, scopeTableCreating); Assert.Equal(0, scopeTableCreated); Assert.Equal(0, scopeLoading); Assert.Equal(0, scopeLoaded); Assert.Equal(1, scopeSaving); Assert.Equal(1, scopeSaved); HelperDatabase.DropDatabase(ProviderType.Sql, dbName); }