diff --git a/Projects/Dotmim.Sync.Core/DbSyncAdapter.cs b/Projects/Dotmim.Sync.Core/DbSyncAdapter.cs index 57b25ca22..470ae3ac0 100644 --- a/Projects/Dotmim.Sync.Core/DbSyncAdapter.cs +++ b/Projects/Dotmim.Sync.Core/DbSyncAdapter.cs @@ -204,7 +204,7 @@ public static SyncTable CreateChangesTable(SyncTable syncTable, SyncSet owner = changesTable.PrimaryKeys.Add(pkey); // get ordered columns that are mutables and pkeys - var orderedNames = syncTable.GetMutableColumnsWithPrimaryKeys(); + var orderedNames = syncTable.GetMutableColumns(false, true); foreach (var c in orderedNames) changesTable.Columns.Add(c.Clone()); diff --git a/Projects/Dotmim.Sync.Core/Orchestrators/LocalOrchestrator.Provision.cs b/Projects/Dotmim.Sync.Core/Orchestrators/LocalOrchestrator.Provision.cs index 9be53f4d9..4b14f5b7c 100644 --- a/Projects/Dotmim.Sync.Core/Orchestrators/LocalOrchestrator.Provision.cs +++ b/Projects/Dotmim.Sync.Core/Orchestrators/LocalOrchestrator.Provision.cs @@ -38,8 +38,12 @@ public virtual Task ProvisionAsync(SyncSet schema = null, bool overwrit /// Provision the local database based on the orchestrator setup, and the provision enumeration /// /// Provision enumeration to determine which components to apply - public virtual Task ProvisionAsync(SyncProvision provision, bool overwrite = false, ScopeInfo clientScopeInfo = null, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress progress = null) - => this.ProvisionAsync(new SyncSet(this.Setup), provision, overwrite, clientScopeInfo, connection, transaction, cancellationToken, progress); + public virtual async Task ProvisionAsync(SyncProvision provision, bool overwrite = false, ScopeInfo clientScopeInfo = null, DbConnection connection = default, DbTransaction transaction = default, CancellationToken cancellationToken = default, IProgress progress = null) + { + var schema = clientScopeInfo?.Schema != null ? clientScopeInfo.Schema : new SyncSet(this.Setup); + + return await this.ProvisionAsync(schema, provision, overwrite, clientScopeInfo, connection, transaction, cancellationToken, progress); + } /// @@ -70,6 +74,9 @@ public virtual async Task ProvisionAsync(SyncSet schema, SyncProvision clientScopeInfo = await this.InternalGetScopeAsync(this.GetContext(), DbScopeType.Client, this.ScopeName, scopeBuilder, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false); } + if (clientScopeInfo?.Schema != null && schema == null) + schema = clientScopeInfo.Schema; + schema = await InternalProvisionAsync(this.GetContext(), overwrite, schema, this.Setup, provision, clientScopeInfo, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false); await runner.CommitAsync().ConfigureAwait(false); diff --git a/Projects/Dotmim.Sync.Core/Orchestrators/RemoteOrchestrator.Provision.cs b/Projects/Dotmim.Sync.Core/Orchestrators/RemoteOrchestrator.Provision.cs index fe5d8dc6e..ad73da20a 100644 --- a/Projects/Dotmim.Sync.Core/Orchestrators/RemoteOrchestrator.Provision.cs +++ b/Projects/Dotmim.Sync.Core/Orchestrators/RemoteOrchestrator.Provision.cs @@ -42,22 +42,23 @@ public virtual async Task ProvisionAsync(SyncProvision provision, bool { try { + await using var runner = await this.GetConnectionAsync(SyncStage.Provisioning, connection, transaction, cancellationToken, progress).ConfigureAwait(false); // Check incompatibility with the flags if (provision.HasFlag(SyncProvision.ClientScope)) throw new InvalidProvisionForRemoteOrchestratorException(); - // Get server scope if not supplied + var scopeBuilder = this.GetScopeBuilder(this.Options.ScopeInfoTableName); + if (serverScopeInfo == null) { - var scopeBuilder = this.GetScopeBuilder(this.Options.ScopeInfoTableName); - var exists = await this.InternalExistsScopeInfoTableAsync(this.GetContext(), DbScopeType.Server, scopeBuilder, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false); if (exists) serverScopeInfo = await this.InternalGetScopeAsync(this.GetContext(), DbScopeType.Server, this.ScopeName, scopeBuilder, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false); } - var schema = new SyncSet(this.Setup); + + var schema = serverScopeInfo?.Schema != null ? serverScopeInfo?.Schema : new SyncSet(this.Setup); schema = await InternalProvisionAsync(this.GetContext(), overwrite, schema, this.Setup, provision, serverScopeInfo, runner.Connection, runner.Transaction, cancellationToken, progress).ConfigureAwait(false); await runner.CommitAsync().ConfigureAwait(false); diff --git a/Projects/Dotmim.Sync.Core/Set/SyncTable.cs b/Projects/Dotmim.Sync.Core/Set/SyncTable.cs index e8ee19bf3..de928f724 100644 --- a/Projects/Dotmim.Sync.Core/Set/SyncTable.cs +++ b/Projects/Dotmim.Sync.Core/Set/SyncTable.cs @@ -218,14 +218,14 @@ public IEnumerable GetMutableColumns(bool includeAutoIncrement = tru /// /// Get all columns that can be queried /// - public IEnumerable GetMutableColumnsWithPrimaryKeys() - { - foreach (var column in this.Columns.OrderBy(c => c.Ordinal)) - { - if (!column.IsCompute && !column.IsReadOnly) - yield return column; - } - } + //public IEnumerable GetMutableColumnsWithPrimaryKeys() + //{ + // foreach (var column in this.Columns.OrderBy(c => c.Ordinal)) + // { + // if (!column.IsCompute && !column.IsReadOnly) + // yield return column; + // } + //} /// /// Get all columns that are Primary keys, based on the names we have in PrimariKeys property diff --git a/Samples/Dotmim.Sync.SampleConsole/MariaDBSyncDownloadOnlyProvider.cs b/Samples/Dotmim.Sync.SampleConsole/MariaDBSyncDownloadOnlyProvider.cs index a32906b15..b35ef4238 100644 --- a/Samples/Dotmim.Sync.SampleConsole/MariaDBSyncDownloadOnlyProvider.cs +++ b/Samples/Dotmim.Sync.SampleConsole/MariaDBSyncDownloadOnlyProvider.cs @@ -235,7 +235,7 @@ private MySqlCommand CreateUpdateCommand() var allColumnsValuesString = new StringBuilder(); string empty = string.Empty; - foreach (var mutableColumn in this.TableDescription.GetMutableColumnsWithPrimaryKeys()) + foreach (var mutableColumn in this.TableDescription.GetMutableColumns()) { var mutableColumnName = ParserName.Parse(mutableColumn, "`").Quoted().ToString(); var parameterColumnName = ParserName.Parse(mutableColumn, "`").Unquoted().Normalized().ToString(); @@ -275,7 +275,7 @@ private MySqlCommand CreateBulkInitializeCommand(int nbLines = 1) var allColumnsString = new StringBuilder(); string empty = string.Empty; - foreach (var mutableColumn in this.TableDescription.GetMutableColumnsWithPrimaryKeys()) + foreach (var mutableColumn in this.TableDescription.GetMutableColumns()) { var mutableColumnName = ParserName.Parse(mutableColumn, "`").Quoted().ToString(); allColumnsString.Append($"{empty}{mutableColumnName}"); @@ -291,7 +291,7 @@ private MySqlCommand CreateBulkInitializeCommand(int nbLines = 1) { stringBuilder.Append($"{commaValues}("); empty = ""; - foreach (var mutableColumn in this.TableDescription.GetMutableColumnsWithPrimaryKeys()) + foreach (var mutableColumn in this.TableDescription.GetMutableColumns()) { var parameterColumnName = ParserName.Parse(mutableColumn, "`").Unquoted().Normalized().ToString(); stringBuilder.Append($"{empty}@p{i}_{parameterColumnName}"); diff --git a/Samples/Dotmim.Sync.SampleConsole/MySqlSyncDownloadOnlyProvider.cs b/Samples/Dotmim.Sync.SampleConsole/MySqlSyncDownloadOnlyProvider.cs index 0fc2935d3..a861bebbc 100644 --- a/Samples/Dotmim.Sync.SampleConsole/MySqlSyncDownloadOnlyProvider.cs +++ b/Samples/Dotmim.Sync.SampleConsole/MySqlSyncDownloadOnlyProvider.cs @@ -144,7 +144,7 @@ private MySqlCommand CreateUpdateCommand() var setUpdateAllColumnsString = new StringBuilder(); string empty = string.Empty; - foreach (var mutableColumn in this.TableDescription.GetMutableColumnsWithPrimaryKeys()) + foreach (var mutableColumn in this.TableDescription.GetMutableColumns()) { var mutableColumnName = ParserName.Parse(mutableColumn, "`").Quoted().ToString(); var parameterColumnName = ParserName.Parse(mutableColumn, "`").Unquoted().Normalized().ToString(); diff --git a/Samples/Dotmim.Sync.SampleConsole/Program.cs b/Samples/Dotmim.Sync.SampleConsole/Program.cs index 4c798955c..741f3fa1c 100644 --- a/Samples/Dotmim.Sync.SampleConsole/Program.cs +++ b/Samples/Dotmim.Sync.SampleConsole/Program.cs @@ -99,7 +99,7 @@ private static async Task Main(string[] args) //var clientProvider = new SqliteSyncProvider(clientDatabaseName); //var clientProvider = new SqlSyncChangeTrackingProvider(DBHelper.GetDatabaseConnectionString(clientDbName)); - var setup = new SyncSetup(new string[] { "ProductCategory" }); + var setup = new SyncSetup(new string[] { "Product" }); //var options = new SyncOptions() { ProgressLevel = SyncProgressLevel.Information }; //setup.Tables["ProductCategory"].Columns.AddRange(new string[] { "ProductCategoryID", "ParentProductCategoryID", "Name" }); @@ -183,10 +183,54 @@ private static async Task SynchronizeAsync(CoreProvider clientProvider, CoreProv Console.ResetColor(); }); + setup = new SyncSetup(new string[] { "Product" }); // Creating an agent that will handle all the process var agent = new SyncAgent(clientProvider, serverProvider, options, setup); + // get scope based on the setup + // getting the scope will provision the database + // so we will need to deprovision - provision again + var serverScope = await agent.RemoteOrchestrator.GetServerScopeAsync(); + + // Optional Create table on client if not exists + + await agent.LocalOrchestrator.ProvisionAsync(serverScope.Schema, SyncProvision.Table); + + var clientScope = await agent.LocalOrchestrator.GetClientScopeAsync(); + + // Deprovision the scope because we want to replace it after + await agent.RemoteOrchestrator.DeprovisionAsync( + SyncProvision.StoredProcedures | + SyncProvision.TrackingTable | + SyncProvision.Triggers); + + var schema = serverScope.Schema; + + + // Removing the primary key that is a auto inc column + schema.Tables[0].PrimaryKeys.Clear(); + + // Removing the primary key as a column as well + schema.Tables[0].Columns.Remove( + serverScope.Schema.Tables[0].Columns.First(c => c.ColumnName == "ProductID")); + + // Add the unique identifier as primary key + schema.Tables[0].PrimaryKeys.Add("rowguid"); + + // affect temporary schema for provisioning + serverScope.Schema = schema; + clientScope.Schema = schema; + + // Provision + await agent.RemoteOrchestrator.ProvisionAsync(SyncProvision.StoredProcedures | + SyncProvision.TrackingTable | + SyncProvision.Triggers, true, serverScope); + + await agent.LocalOrchestrator.ProvisionAsync(SyncProvision.StoredProcedures | + SyncProvision.TrackingTable | + SyncProvision.Triggers, true, clientScope); + //// This event is raised before selecting the changes for a particular table //// you still can change the DbCommand generated, if you need to @@ -214,41 +258,7 @@ private static async Task SynchronizeAsync(CoreProvider clientProvider, CoreProv //}); - agent.LocalOrchestrator.OnTableChangesSelectedSyncRow(args => - { - if (args.SyncRow.RowState == DataRowState.Deleted) - args.SyncRow = null; - }); - agent.RemoteOrchestrator.OnTableChangesApplyingSyncRows(async args => - { - if (args.SchemaTable.TableName == "ProductCategory") - { - var cmd = args.Connection.CreateCommand(); - cmd.CommandText = "Select count(*) from ProductCategory_tracking where ProductCategoryID = @ProductCategoryID"; - cmd.Connection = args.Connection; - cmd.Transaction = args.Transaction; - var p = cmd.CreateParameter(); - p.DbType = DbType.Guid; - p.ParameterName = "@ProductCategoryID"; - cmd.Parameters.Add(p); - - foreach (var row in args.SyncRows.ToArray()) - { - if (row.RowState == DataRowState.Modified) - { - cmd.Parameters[0].Value = new Guid(row["ProductCategoryID"].ToString()); - var alreadyExists = await cmd.ExecuteScalarAsync(); - - if ((int)alreadyExists == 1) - { - args.SyncRows.Remove(row); - } - } - } - - } - }); //// The table is read. The batch parts infos are generated and already available on disk //agent.LocalOrchestrator.OnTableChangesSelected(async tcsa =>