private static async Task DeprovisionServerManuallyAsync() { // Create server provider var serverProvider = new SqlSyncProvider(serverConnectionString); // Create standard Setup and Options var setup = new SyncSetup(new string[] { "Address", "Customer", "CustomerAddress" }); var options = new SyncOptions(); // Create a server orchestrator used to Deprovision everything on the server side var remoteOrchestrator = new RemoteOrchestrator(serverProvider, options, setup); // Get the server scope var serverScope = await remoteOrchestrator.GetServerScopeAsync(); // Deprovision everything await remoteOrchestrator.DeprovisionAsync(SyncProvision.StoredProcedures | SyncProvision.Triggers | SyncProvision.TrackingTable); // Affect good values serverScope.Setup = null; serverScope.Schema = null; // save the server scope await remoteOrchestrator.SaveServerScopeAsync(serverScope); }
public void SyncAgent_EighthConstructor_LocalOrchestrator_ShouldMatch_RemoteOrchestrator_With_ScopeNameDefined() { var clientProvider = new SqlSyncProvider(); var options = new SyncOptions(); var setup = new SyncSetup(new string[] { "Customer" }); // this options and setup will be overriden by the constructor var remoteOptions = new SyncOptions(); var remoteSetup = new SyncSetup(new string[] { "Product", "ProductCategory" }); var remoteOrchestrator = new RemoteOrchestrator(new SqlSyncProvider(), remoteOptions, remoteSetup); var agent = new SyncAgent(clientProvider, remoteOrchestrator, options, setup, "CustomerScope"); this.CheckConstructor(agent); Assert.Same(options, agent.LocalOrchestrator.Options); Assert.Same(options, agent.RemoteOrchestrator.Options); Assert.Same(setup, agent.LocalOrchestrator.Setup); Assert.Same(setup, agent.RemoteOrchestrator.Setup); Assert.Equal("CustomerScope", agent.ScopeName); Assert.Equal("CustomerScope", agent.LocalOrchestrator.ScopeName); Assert.Equal("CustomerScope", agent.RemoteOrchestrator.ScopeName); Assert.Single(agent.LocalOrchestrator.Setup.Tables); Assert.Single(agent.RemoteOrchestrator.Setup.Tables); Assert.Equal("Customer", agent.LocalOrchestrator.Setup.Tables[0].TableName); Assert.Equal("Customer", agent.RemoteOrchestrator.Setup.Tables[0].TableName); }
public async Task Trigger_Exists() { 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); // Create default table var ctx = new AdventureWorksContext((dbName, ProviderType.Sql, sqlProvider), true, false); await ctx.Database.EnsureCreatedAsync(); var options = new SyncOptions(); var setup = new SyncSetup(new string[] { "SalesLT.Product" }); var remoteOrchestrator = new RemoteOrchestrator(sqlProvider, options); var scopeInfo = await remoteOrchestrator.GetServerScopeInfoAsync(setup); await remoteOrchestrator.CreateTriggerAsync(scopeInfo, "Product", "SalesLT", DbTriggerType.Insert); var exists = await remoteOrchestrator.ExistTriggerAsync(scopeInfo, "Product", "SalesLT", DbTriggerType.Insert); Assert.True(exists); exists = await remoteOrchestrator.ExistTriggerAsync(scopeInfo, "Product", "SalesLT", DbTriggerType.Update); Assert.False(exists); 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); // ----------------------------------------------------------------- // 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 ServerScopeInfo from the server side using a RemoteOrchestrator or a WebRemoteOrchestrator // - Provision everything locally // Create a local orchestrator used to provision everything locally var localOrchestrator = new LocalOrchestrator(clientProvider); // Because we need the schema from remote side, create a remote orchestrator var remoteOrchestrator = new RemoteOrchestrator(serverProvider); // Getting the server scope from server side var serverScope = await remoteOrchestrator.GetServerScopeInfoAsync(); // You can create a WebRemoteOrchestrator and get the ServerScope as well // var proxyClientProvider = new WebRemoteOrchestrator("https://localhost:44369/api/Sync"); // var serverScope = proxyClientProvider.GetServerScopeInfoAsync(); // Provision everything needed (sp, triggers, tracking tables, AND TABLES) await localOrchestrator.ProvisionAsync(serverScope); }
public async Task RemoteOrchestrator_Provision_SchemaCreated_If_SetupHasTables() { 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(new string[] { "SalesLT.Product" }); var remoteOrchestrator = new RemoteOrchestrator(sqlProvider, options); var provision = SyncProvision.Table | SyncProvision.TrackingTable | SyncProvision.StoredProcedures | SyncProvision.Triggers; var clientScopeInfo = await remoteOrchestrator.ProvisionAsync(scopeName, setup, provision); Assert.Single(clientScopeInfo.Schema.Tables); Assert.Equal("SalesLT.Product", clientScopeInfo.Schema.Tables[0].GetFullName()); Assert.Equal(17, clientScopeInfo.Schema.Tables[0].Columns.Count); HelperDatabase.DropDatabase(ProviderType.Sql, dbName); }
public async Task RemoteOrchestrator_Provision_SchemaNotCreated_If_SetupHasTables_AndDbIsEmpty() { 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 scopeName = "scope"; var options = new SyncOptions(); var setup = new SyncSetup(new string[] { "SalesLT.Product" }); var remoteOrchestrator = new RemoteOrchestrator(sqlProvider, options); var provision = SyncProvision.Table | SyncProvision.TrackingTable | SyncProvision.StoredProcedures | SyncProvision.Triggers; var se = await Assert.ThrowsAsync <SyncException>( async() => await remoteOrchestrator.ProvisionAsync(scopeName, setup, provision)); Assert.Equal(SyncStage.Provisioning, se.SyncStage); Assert.Equal(SyncSide.ServerSide, se.Side); Assert.Equal("MissingTableException", se.TypeName); HelperDatabase.DropDatabase(ProviderType.Sql, dbName); }
public async Task RemoteOrchestrator_GetServerScopeInfo_NonExistingTables_ShouldFail() { 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(); // Create a bad setup with a non existing table var tables = new string[] { "SalesLT.ProductCategory", "SalesLT.ProductModel", "SalesLT.Product", "Employee", "Customer", "Address", "CustomerAddress", "EmployeeAddress", "SalesLT.SalesOrderHeader", "SalesLT.SalesOrderDetail", "Posts", "Tags", "PostTag", "PricesList", "PricesListCategory", "PricesListDetail", "WRONGTABLE" }; var setup = new SyncSetup(tables); var remoteOrchestrator = new RemoteOrchestrator(sqlProvider, options); var se = await Assert.ThrowsAsync <SyncException>(async() => { await remoteOrchestrator.GetServerScopeInfoAsync(setup); }); Assert.Equal(SyncStage.Provisioning, se.SyncStage); Assert.Equal(SyncSide.ServerSide, se.Side); Assert.Equal("MissingTableException", se.TypeName); HelperDatabase.DropDatabase(ProviderType.Sql, dbName); }
public async Task RemoteOrchestrator_Provision_ShouldFail_If_SetupIsEmpty() { 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(); var orchestrator = new RemoteOrchestrator(sqlProvider, options); var provision = SyncProvision.Table | SyncProvision.TrackingTable | SyncProvision.StoredProcedures | SyncProvision.Triggers; var se = await Assert.ThrowsAsync <SyncException>( async() => await orchestrator.ProvisionAsync(scopeName, setup, provision)); Assert.Equal(SyncStage.ScopeLoading, se.SyncStage); Assert.Equal(SyncSide.ServerSide, se.Side); Assert.Equal("MissingServerScopeTablesException", se.TypeName); HelperDatabase.DropDatabase(ProviderType.Sql, dbName); }
public async Task RemoteOrchestrator_GetServerScopeInfo_SetupColumnsDefined_ShouldReturn_SchemaWithSetupColumnsOnly() { 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(); // Create a bad setup with a non existing table var tables = new string[] { "Customer", "Address", "CustomerAddress" }; var setup = new SyncSetup(tables); setup.Tables["Customer"].Columns.AddRange(new string[] { "CustomerID", "FirstName", "LastName", "CompanyName" }); var remoteOrchestrator = new RemoteOrchestrator(sqlProvider, options); var scopeInfo = await remoteOrchestrator.GetServerScopeInfoAsync(setup); Assert.Equal(3, scopeInfo.Schema.Tables.Count); // Only 4 columns shoud be part of Customer table Assert.Equal(4, scopeInfo.Schema.Tables["Customer"].Columns.Count); Assert.Equal(9, scopeInfo.Schema.Tables["Address"].Columns.Count); Assert.Equal(5, scopeInfo.Schema.Tables["CustomerAddress"].Columns.Count); HelperDatabase.DropDatabase(ProviderType.Sql, dbName); }
public async Task RemoteOrchestrator_GetServerScopeInfo_NonExistingColumns_ShouldFail() { 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(); // Create a bad setup with a non existing table var tables = new string[] { "Customer", "Address", "CustomerAddress" }; var setup = new SyncSetup(tables); setup.Tables["Customer"].Columns.AddRange(new string[] { "FirstName", "LastName", "CompanyName", "BADCOLUMN" }); var remoteOrchestrator = new RemoteOrchestrator(sqlProvider, options); var se = await Assert.ThrowsAsync <SyncException>(async() => { await remoteOrchestrator.GetServerScopeInfoAsync(setup); }); Assert.Equal(SyncStage.Provisioning, se.SyncStage); Assert.Equal(SyncSide.ServerSide, se.Side); Assert.Equal("MissingColumnException", se.TypeName); HelperDatabase.DropDatabase(ProviderType.Sql, dbName); }
public async Task RemoteOrchestrator_StoredProcedure_Exists() { 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); // Create default table 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(new string[] { "SalesLT.Product" }) { StoredProceduresPrefix = "sp_", StoredProceduresSuffix = "_sp" }; var remoteOrchestrator = new RemoteOrchestrator(sqlProvider, options); var scopeInfo = await remoteOrchestrator.GetServerScopeInfoAsync(scopeName, setup); await remoteOrchestrator.CreateStoredProcedureAsync(scopeInfo, "Product", "SalesLT", DbStoredProcedureType.SelectChanges, false); Assert.True(await remoteOrchestrator.ExistStoredProcedureAsync(scopeInfo, "Product", "SalesLT", DbStoredProcedureType.SelectChanges)); Assert.False(await remoteOrchestrator.ExistStoredProcedureAsync(scopeInfo, "Product", "SalesLT", DbStoredProcedureType.UpdateRow)); HelperDatabase.DropDatabase(ProviderType.Sql, dbName); }
public async Task RemoteOrchestrator_Scope_IsNotNewScope_OnceSaved() { var dbName = HelperDatabase.GetRandomName("tcp_ro_"); 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 remoteOrchestrator = new RemoteOrchestrator(sqlProvider, options); var remoteScopeInfo = await remoteOrchestrator.GetServerScopeInfoAsync(scopeName, setup); Assert.True(remoteScopeInfo.IsNewScope); Assert.Equal(SyncVersion.Current, new Version(remoteScopeInfo.Version)); remoteScopeInfo = await remoteOrchestrator.SaveServerScopeInfoAsync(remoteScopeInfo); Assert.False(remoteScopeInfo.IsNewScope); HelperDatabase.DropDatabase(ProviderType.Sql, dbName); }
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); }
public override RemoteOrchestrator CreateRemoteOrchestrator(ProviderType providerType, string dbName) { var cs = HelperDatabase.GetConnectionString(ProviderType.MySql, dbName); var orchestrator = new RemoteOrchestrator(new MySqlSyncProvider(cs)); return(orchestrator); }
private static async Task CreateSnapshotAsync() { // Create 2 Sql Sync providers var serverProvider = new SqlSyncProvider(DBHelper.GetDatabaseConnectionString(serverDbName)); // specific Setup with only 2 tables, and one filtered var setup = new SyncSetup(allTables); // 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(); }); // snapshot directory var directory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Snapshots"); var options = new SyncOptions { SnapshotsDirectory = directory, BatchSize = 2000 }; var remoteOrchestrator = new RemoteOrchestrator(serverProvider, options, setup); await remoteOrchestrator.CreateSnapshotAsync(null, default, remoteProgress);
public async Task Table_Drop_All() { 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 remoteOrchestrator = new RemoteOrchestrator(sqlProvider, options); var onDropping = 0; var onDropped = 0; var scopeInfo = await remoteOrchestrator.GetServerScopeInfoAsync(setup); remoteOrchestrator.OnTableDropping(ttca => onDropping++); remoteOrchestrator.OnTableDropped(ttca => onDropped++); await remoteOrchestrator.DropTablesAsync(scopeInfo); Assert.Equal(this.Tables.Length, onDropping); Assert.Equal(this.Tables.Length, onDropped); HelperDatabase.DropDatabase(ProviderType.Sql, dbName); }
public async Task RemoteOrchestrator_Provision_ShouldFails_If_SetupTable_DoesNotExist() { 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(new string[] { "SalesLT.badTable" }); var remoteOrchestrator = new RemoteOrchestrator(sqlProvider, options); var se = await Assert.ThrowsAsync <SyncException>(async() => await remoteOrchestrator.GetServerScopeInfoAsync(scopeName, setup)); Assert.Equal(SyncStage.Provisioning, se.SyncStage); Assert.Equal(SyncSide.ServerSide, se.Side); Assert.Equal("MissingTableException", se.TypeName); HelperDatabase.DropDatabase(ProviderType.Sql, dbName); }
public async Task RemoteOrchestrator_Provision_ShouldCreate_StoredProcedures_WithSpecificScopeName() { 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); // Create default table 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(new string[] { "SalesLT.Product" }) { StoredProceduresPrefix = "s", StoredProceduresSuffix = "proc" }; // trackign table name is composed with prefix and suffix from setup var bulkDelete = $"SalesLT.{setup.StoredProceduresPrefix}Product{setup.StoredProceduresSuffix}_{scopeName}_bulkdelete"; var bulkUpdate = $"SalesLT.{setup.StoredProceduresPrefix}Product{setup.StoredProceduresSuffix}_{scopeName}_bulkupdate"; var changes = $"SalesLT.{setup.StoredProceduresPrefix}Product{setup.StoredProceduresSuffix}_{scopeName}_changes"; var delete = $"SalesLT.{setup.StoredProceduresPrefix}Product{setup.StoredProceduresSuffix}_{scopeName}_delete"; var deletemetadata = $"SalesLT.{setup.StoredProceduresPrefix}Product{setup.StoredProceduresSuffix}_{scopeName}_deletemetadata"; var initialize = $"SalesLT.{setup.StoredProceduresPrefix}Product{setup.StoredProceduresSuffix}_{scopeName}_initialize"; var reset = $"SalesLT.{setup.StoredProceduresPrefix}Product{setup.StoredProceduresSuffix}_{scopeName}_reset"; var selectrow = $"SalesLT.{setup.StoredProceduresPrefix}Product{setup.StoredProceduresSuffix}_{scopeName}_selectrow"; var update = $"SalesLT.{setup.StoredProceduresPrefix}Product{setup.StoredProceduresSuffix}_{scopeName}_update"; var remoteOrchestrator = new RemoteOrchestrator(sqlProvider, options); // Needs the tracking table to be able to create stored procedures var provision = SyncProvision.TrackingTable | SyncProvision.StoredProcedures; var scopeInfo = await remoteOrchestrator.GetServerScopeInfoAsync(scopeName, setup); await remoteOrchestrator.ProvisionAsync(scopeInfo, provision); using (var connection = new SqlConnection(cs)) { await connection.OpenAsync().ConfigureAwait(false); Assert.True(await SqlManagementUtils.ProcedureExistsAsync(connection, null, bulkDelete)); Assert.True(await SqlManagementUtils.ProcedureExistsAsync(connection, null, bulkUpdate)); Assert.True(await SqlManagementUtils.ProcedureExistsAsync(connection, null, changes)); Assert.True(await SqlManagementUtils.ProcedureExistsAsync(connection, null, delete)); Assert.True(await SqlManagementUtils.ProcedureExistsAsync(connection, null, deletemetadata)); Assert.True(await SqlManagementUtils.ProcedureExistsAsync(connection, null, initialize)); Assert.True(await SqlManagementUtils.ProcedureExistsAsync(connection, null, reset)); Assert.True(await SqlManagementUtils.ProcedureExistsAsync(connection, null, selectrow)); Assert.True(await SqlManagementUtils.ProcedureExistsAsync(connection, null, update)); connection.Close(); } HelperDatabase.DropDatabase(ProviderType.Sql, dbName); }
public async Task TrackingTable_Create_One() { 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(new string[] { "SalesLT.Product" }) { TrackingTablesPrefix = "t_", TrackingTablesSuffix = "_t" }; var remoteOrchestrator = new RemoteOrchestrator(sqlProvider, options); var scopeInfo = await remoteOrchestrator.GetServerScopeInfoAsync(setup); var onCreating = false; var onCreated = false; remoteOrchestrator.OnTrackingTableCreating(ttca => { var addingID = $" ALTER TABLE {ttca.TrackingTableName.Schema().Quoted()} ADD internal_id int identity(1,1)"; ttca.Command.CommandText += addingID; onCreating = true; }); remoteOrchestrator.OnTrackingTableCreated(ttca => { onCreated = true; }); await remoteOrchestrator.CreateTrackingTableAsync(scopeInfo, "Product", "SalesLT"); Assert.True(onCreating); Assert.True(onCreated); // Check we have a new column in tracking table using (var c = new SqlConnection(cs)) { await c.OpenAsync().ConfigureAwait(false); var cols = await SqlManagementUtils.GetColumnsForTableAsync(c, null, "t_Product_t", "SalesLT").ConfigureAwait(false); Assert.Equal(7, cols.Rows.Count); Assert.NotNull(cols.Rows.FirstOrDefault(r => r["name"].ToString() == "internal_id")); c.Close(); } HelperDatabase.DropDatabase(ProviderType.Sql, dbName); }
public async Task Trigger_Create_One() { 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); // Create default table var ctx = new AdventureWorksContext((dbName, ProviderType.Sql, sqlProvider), true, false); await ctx.Database.EnsureCreatedAsync(); var options = new SyncOptions(); var setup = new SyncSetup(new string[] { "SalesLT.Product" }); // 1) create a console logger //var loggerFactory = LoggerFactory.Create(builder => { builder.AddDebug().SetMinimumLevel(LogLevel.Debug); }); //var logger = loggerFactory.CreateLogger("Dotmim.Sync"); var logger = new SyncLogger().AddDebug().SetMinimumLevel(LogLevel.Debug); options.Logger = logger; var remoteOrchestrator = new RemoteOrchestrator(sqlProvider, options); var scopeInfo = await remoteOrchestrator.GetServerScopeInfoAsync(setup); var onCreating = 0; var onCreated = 0; var onDropping = 0; var onDropped = 0; remoteOrchestrator.OnTriggerCreating(tca => onCreating++); remoteOrchestrator.OnTriggerCreated(tca => onCreated++); remoteOrchestrator.OnTriggerDropping(tca => onDropping++); remoteOrchestrator.OnTriggerDropped(tca => onDropped++); var isCreated = await remoteOrchestrator.CreateTriggerAsync(scopeInfo, "Product", "SalesLT", DbTriggerType.Insert); Assert.True(isCreated); Assert.Equal(1, onCreating); Assert.Equal(1, onCreated); Assert.Equal(0, onDropping); Assert.Equal(0, onDropped); // Check using (var c = new SqlConnection(cs)) { await c.OpenAsync().ConfigureAwait(false); var check = await SqlManagementUtils.GetTriggerAsync(c, null, "Product_insert_trigger", "SalesLT").ConfigureAwait(false); Assert.Single(check.Rows); c.Close(); } HelperDatabase.DropDatabase(ProviderType.Sql, dbName); }
private static async Task DropSync() { var provider = new SqlSyncProvider(serverConnectionString); var options = new SyncOptions(); var setup = new SyncSetup("ProductCategory", "ProductModel", "Product"); var orchestrator = new RemoteOrchestrator(provider, options); await orchestrator.DropAllAsync(); }
public async Task Trigger_Create_All() { 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); // Create default table var ctx = new AdventureWorksContext((dbName, ProviderType.Sql, sqlProvider), true, false); await ctx.Database.EnsureCreatedAsync(); var options = new SyncOptions(); var setup = new SyncSetup(new string[] { "SalesLT.Product" }); var remoteOrchestrator = new RemoteOrchestrator(sqlProvider, options); var scopeInfo = await remoteOrchestrator.GetServerScopeInfoAsync(setup); var onCreating = 0; var onCreated = 0; var onDropping = 0; var onDropped = 0; remoteOrchestrator.OnTriggerCreating(tca => onCreating++); remoteOrchestrator.OnTriggerCreated(tca => onCreated++); remoteOrchestrator.OnTriggerDropping(tca => onDropping++); remoteOrchestrator.OnTriggerDropped(tca => onDropped++); var isCreated = await remoteOrchestrator.CreateTriggersAsync(scopeInfo, "Product", "SalesLT"); Assert.True(isCreated); Assert.Equal(3, onCreating); Assert.Equal(3, onCreated); Assert.Equal(0, onDropping); Assert.Equal(0, onDropped); // Check using (var c = new SqlConnection(cs)) { await c.OpenAsync().ConfigureAwait(false); var check = await SqlManagementUtils.GetTriggerAsync(c, null, "Product_insert_trigger", "SalesLT").ConfigureAwait(false); Assert.Single(check.Rows); check = await SqlManagementUtils.GetTriggerAsync(c, null, "Product_update_trigger", "SalesLT").ConfigureAwait(false); Assert.Single(check.Rows); check = await SqlManagementUtils.GetTriggerAsync(c, null, "Product_delete_trigger", "SalesLT").ConfigureAwait(false); Assert.Single(check.Rows); c.Close(); } HelperDatabase.DropDatabase(ProviderType.Sql, dbName); }
public async Task RemoteOrchestrator_Provision_ShouldCreate_Triggers() { 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); // Create default table 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(new string[] { "SalesLT.Product" }) { TrackingTablesSuffix = "sync", TrackingTablesPrefix = "trck", TriggersPrefix = "trg_", TriggersSuffix = "_trg" }; // trackign table name is composed with prefix and suffix from setup var triggerDelete = $"{setup.TriggersPrefix}Product{setup.TriggersSuffix}_delete_trigger"; var triggerInsert = $"{setup.TriggersPrefix}Product{setup.TriggersSuffix}_insert_trigger"; var triggerUpdate = $"{setup.TriggersPrefix}Product{setup.TriggersSuffix}_update_trigger"; var remoteOrchestrator = new RemoteOrchestrator(sqlProvider, options); var scopeInfo = await remoteOrchestrator.GetServerScopeInfoAsync(scopeName, setup); // Needs the tracking table to be able to create triggers var provision = SyncProvision.TrackingTable | SyncProvision.Triggers; await remoteOrchestrator.ProvisionAsync(scopeInfo, provision); using (var c = new SqlConnection(cs)) { await c.OpenAsync().ConfigureAwait(false); var trigDel = await SqlManagementUtils.GetTriggerAsync(c, null, triggerDelete, "SalesLT"); Assert.Equal(triggerDelete, trigDel.Rows[0]["Name"].ToString()); var trigIns = await SqlManagementUtils.GetTriggerAsync(c, null, triggerInsert, "SalesLT"); Assert.Equal(triggerInsert, trigIns.Rows[0]["Name"].ToString()); var trigUdate = await SqlManagementUtils.GetTriggerAsync(c, null, triggerUpdate, "SalesLT"); Assert.Equal(triggerUpdate, trigUdate.Rows[0]["Name"].ToString()); c.Close(); } HelperDatabase.DropDatabase(ProviderType.Sql, dbName); }
public async Task TrackingTable_Drop_One_Cancel() { 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(new string[] { "SalesLT.Product" }); setup.TrackingTablesPrefix = "t_"; setup.TrackingTablesSuffix = "_t"; var remoteOrchestrator = new RemoteOrchestrator(sqlProvider, options); var scopeInfo = await remoteOrchestrator.GetServerScopeInfoAsync(setup); var onDropping = false; var onDropped = false; remoteOrchestrator.OnTrackingTableDropping(ttca => { ttca.Cancel = true; onDropping = true; }); remoteOrchestrator.OnTrackingTableDropped(ttca => { onDropped = true; }); await remoteOrchestrator.CreateTrackingTableAsync(scopeInfo, "Product", "SalesLT"); await remoteOrchestrator.DropTrackingTableAsync(scopeInfo, "Product", "SalesLT"); Assert.True(onDropping); Assert.False(onDropped); // Check we have a new column in tracking table using (var c = new SqlConnection(cs)) { await c.OpenAsync().ConfigureAwait(false); var table = await SqlManagementUtils.GetTableAsync(c, null, "t_Product_t", "SalesLT").ConfigureAwait(false); Assert.NotEmpty(table.Rows); c.Close(); } HelperDatabase.DropDatabase(ProviderType.Sql, dbName); }
public async Task RemoteOrchestrator_GetServerScopeInfo_ShouldReturnSchema() { 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 onSchemaRead = false; var onSchemaReading = false; var remoteOrchestrator = new RemoteOrchestrator(sqlProvider, options); remoteOrchestrator.OnSchemaLoading(args => { Assert.Equal(scopeName, args.Context.ScopeName); onSchemaReading = true; }); remoteOrchestrator.OnSchemaLoaded(args => { Assert.IsType <SchemaLoadedArgs>(args); Assert.Equal(SyncStage.Provisioning, args.Context.SyncStage); Assert.Equal(scopeName, args.Context.ScopeName); Assert.NotNull(args.Connection); Assert.Null(args.Transaction); Assert.Equal(ConnectionState.Open, args.Connection.State); Assert.Equal(16, args.Schema.Tables.Count); onSchemaRead = true; }); AssertConnectionAndTransaction(remoteOrchestrator, scopeName); var scopeInfo = await remoteOrchestrator.GetServerScopeInfoAsync(scopeName, setup); Assert.NotNull(scopeInfo.Schema); Assert.NotNull(scopeInfo.Setup); Assert.Equal(16, scopeInfo.Schema.Tables.Count); Assert.True(onSchemaRead); Assert.True(onSchemaReading); var schema = await remoteOrchestrator.GetSchemaAsync(scopeName, setup); Assert.NotNull(schema); Assert.Equal(16, schema.Tables.Count); HelperDatabase.DropDatabase(ProviderType.Sql, dbName); }
public async Task RemoteOrchestrator_GetEstimatedChanges_BeforeInitialize_ShouldReturnRowsCount() { var dbNameSrv = HelperDatabase.GetRandomName("tcp_lo_srv"); await HelperDatabase.CreateDatabaseAsync(ProviderType.Sql, dbNameSrv, true); var csServer = HelperDatabase.GetConnectionString(ProviderType.Sql, dbNameSrv); var serverProvider = new SqlSyncProvider(csServer); await new AdventureWorksContext((dbNameSrv, ProviderType.Sql, serverProvider), true, false).Database.EnsureCreatedAsync(); var scopeName = "scopesnap1"; var syncOptions = new SyncOptions(); var setup = new SyncSetup(); var remoteOrchestrator = new RemoteOrchestrator(serverProvider, new SyncOptions(), new SyncSetup(this.Tables), scopeName); // Server side : Create a product category and a product // Create a productcategory item // Create a new product on server var productId = Guid.NewGuid(); var productName = HelperDatabase.GetRandomName(); var productNumber = productName.ToUpperInvariant().Substring(0, 10); var productCategoryName = HelperDatabase.GetRandomName(); var productCategoryId = productCategoryName.ToUpperInvariant().Substring(0, 6); using (var ctx = new AdventureWorksContext((dbNameSrv, ProviderType.Sql, serverProvider))) { var pc = new ProductCategory { ProductCategoryId = productCategoryId, Name = productCategoryName }; ctx.Add(pc); var product = new Product { ProductId = productId, Name = productName, ProductNumber = productNumber }; ctx.Add(product); await ctx.SaveChangesAsync(); } // fake client scope var clientScope = new ScopeInfo() { Name = scopeName, IsNewScope = true }; // Get estimated changes count to be sent to the client var changes = await remoteOrchestrator.GetEstimatedChangesCountAsync(clientScope); Assert.NotNull(changes.ServerChangesSelected); Assert.Equal(2, changes.ServerChangesSelected.TableChangesSelected.Count); Assert.Contains("Product", changes.ServerChangesSelected.TableChangesSelected.Select(tcs => tcs.TableName).ToList()); Assert.Contains("ProductCategory", changes.ServerChangesSelected.TableChangesSelected.Select(tcs => tcs.TableName).ToList()); }
public async Task <IActionResult> CreateSnapshotAsync() { var connectionString = configuration.GetConnectionString("DefaultConnection"); var serverProvider = new SqlSyncProvider(connectionString); // Create a remote orchestrator var remoteOrchestrator = new RemoteOrchestrator(serverProvider, syncOptions, syncSetup); // Create a snapshot await remoteOrchestrator.CreateSnapshotAsync(); return(Ok()); }
public async Task Trigger_Drop_All() { 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); // Create default table var ctx = new AdventureWorksContext((dbName, ProviderType.Sql, sqlProvider), true, false); await ctx.Database.EnsureCreatedAsync(); var options = new SyncOptions(); var setup = new SyncSetup(new string[] { "SalesLT.Product" }); var remoteOrchestrator = new RemoteOrchestrator(sqlProvider, options); var scopeInfo = await remoteOrchestrator.GetServerScopeInfoAsync(setup); var onCreating = 0; var onCreated = 0; var onDropping = 0; var onDropped = 0; remoteOrchestrator.OnTriggerCreating(tca => onCreating++); remoteOrchestrator.OnTriggerCreated(tca => onCreated++); remoteOrchestrator.OnTriggerDropping(tca => onDropping++); remoteOrchestrator.OnTriggerDropped(tca => onDropped++); var isCreated = await remoteOrchestrator.CreateTriggersAsync(scopeInfo, "Product", "SalesLT"); Assert.True(isCreated); Assert.Equal(3, onCreating); Assert.Equal(3, onCreated); Assert.Equal(0, onDropping); Assert.Equal(0, onDropped); onCreating = 0; onCreated = 0; onDropping = 0; onDropped = 0; var isDropped = await remoteOrchestrator.DropTriggersAsync(scopeInfo, "Product", "SalesLT"); Assert.True(isCreated); Assert.Equal(0, onCreating); Assert.Equal(0, onCreated); Assert.Equal(3, onDropping); Assert.Equal(3, onDropped); HelperDatabase.DropDatabase(ProviderType.Sql, dbName); }
public async Task RemoteOrchestrator_CreateSnapshot_ShouldFail_If_MissingMandatoriesOptions() { 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, true); await ctx.Database.EnsureCreatedAsync(); var scopeName = "scopesnap"; // snapshot directory var snapshotDirctoryName = HelperDatabase.GetRandomName(); var snapshotDirectory = Path.Combine(Environment.CurrentDirectory, snapshotDirctoryName); var options = new SyncOptions { SnapshotsDirectory = snapshotDirectory }; var setup = new SyncSetup(Tables); var provider = new SqlSyncProvider(cs); var orchestrator = new RemoteOrchestrator(provider, options, setup, scopeName); var se = await Assert.ThrowsAsync <SyncException>(() => orchestrator.CreateSnapshotAsync()); Assert.Equal(SyncStage.SnapshotCreating, se.SyncStage); Assert.Equal(SyncSide.ServerSide, se.Side); Assert.Equal("SnapshotMissingMandatariesOptionsException", se.TypeName); options = new SyncOptions { BatchSize = 2000 }; orchestrator = new RemoteOrchestrator(provider, options, setup, scopeName); se = await Assert.ThrowsAsync <SyncException>(() => orchestrator.CreateSnapshotAsync()); Assert.Equal(SyncStage.SnapshotCreating, se.SyncStage); Assert.Equal(SyncSide.ServerSide, se.Side); Assert.Equal("SnapshotMissingMandatariesOptionsException", se.TypeName); options = new SyncOptions { }; orchestrator = new RemoteOrchestrator(provider, options, setup, scopeName); se = await Assert.ThrowsAsync <SyncException>(() => orchestrator.CreateSnapshotAsync()); Assert.Equal(SyncStage.SnapshotCreating, se.SyncStage); Assert.Equal(SyncSide.ServerSide, se.Side); Assert.Equal("SnapshotMissingMandatariesOptionsException", se.TypeName); 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); }