/// <summary>
        /// Ensure configuration is correct on both server and client side
        /// </summary>
        public virtual async Task <(SyncContext, SyncConfiguration)> EnsureConfigurationAsync(
            SyncContext context, SyncConfiguration syncConfiguration = null)
        {
            try
            {
                context.SyncStage = SyncStage.ConfigurationApplying;

                // Get cache manager and try to get configuration from cache
                var cacheManager       = this.CacheManager;
                var cacheConfiguration = GetCacheConfiguration();

                // if we don't pass config object (configuration == null), we may be in proxy mode, so the config object is handled by a local configuration object.
                if (syncConfiguration == null && this.syncConfiguration == null)
                {
                    throw new ArgumentNullException("syncConfiguration", "You try to set a provider with no configuration object");
                }

                // the configuration has been set from the proxy server itself, use it.
                if (syncConfiguration == null && this.syncConfiguration != null)
                {
                    syncConfiguration = this.syncConfiguration;
                }

                // Raise event before
                context.SyncStage = SyncStage.ConfigurationApplying;
                var beforeArgs2 = new ConfigurationApplyingEventArgs(this.ProviderTypeName, context.SyncStage);
                this.TryRaiseProgressEvent(beforeArgs2, this.ConfigurationApplying);
                bool overWriteConfiguration = beforeArgs2.OverwriteConfiguration;

                // if we have already a cache configuration, we can return, except if we should overwrite it
                if (cacheConfiguration != null && !overWriteConfiguration)
                {
                    // Raise event after
                    context.SyncStage = SyncStage.ConfigurationApplied;
                    var afterArgs2 = new ConfigurationAppliedEventArgs(this.ProviderTypeName, context.SyncStage, cacheConfiguration);
                    this.TryRaiseProgressEvent(afterArgs2, this.ConfigurationApplied);

                    // if config has been changed by user, save it again
                    this.SetCacheConfiguration(cacheConfiguration);
                    return(context, cacheConfiguration);
                }

                // create local directory
                if (!String.IsNullOrEmpty(syncConfiguration.BatchDirectory) && !Directory.Exists(syncConfiguration.BatchDirectory))
                {
                    Directory.CreateDirectory(syncConfiguration.BatchDirectory);
                }

                // if we dont have already read the tables || we want to overwrite the current config
                if ((syncConfiguration.HasTables && !syncConfiguration.HasColumns))
                {
                    await this.ReadConfigurationAsync(syncConfiguration);
                }

                // save to cache
                this.SetCacheConfiguration(syncConfiguration);

                context.SyncStage = SyncStage.ConfigurationApplied;
                var afterArgs = new ConfigurationAppliedEventArgs(this.ProviderTypeName, context.SyncStage, syncConfiguration);
                this.TryRaiseProgressEvent(afterArgs, this.ConfigurationApplied);
                // if config has been changed by user, save it again
                this.SetCacheConfiguration(syncConfiguration);
                return(context, syncConfiguration);
            }
            catch (SyncException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new SyncException(ex, SyncStage.ConfigurationApplying, this.ProviderTypeName);
            }
        }
        /// <summary>
        /// Ensure configuration is correct on both server and client side
        /// </summary>
        public virtual async Task <(SyncContext, SyncConfiguration)> EnsureConfigurationAsync(SyncContext context,
                                                                                              SyncConfiguration configuration = null)
        {
            try
            {
                context.SyncStage = SyncStage.ConfigurationApplying;

                // Get cache manager and try to get configuration from cache
                var cacheManager       = this.CacheManager;
                var cacheConfiguration = GetCacheConfiguration();

                // if we don't pass config object (configuration == null), we may be in proxy mode, so the config object is handled by a local configuration object.
                if (configuration == null && this.syncConfiguration == null)
                {
                    throw SyncException.CreateArgumentException(SyncStage.ConfigurationApplied, "Configuration", "You try to set a provider with no configuration object");
                }

                // the configuration has been set from the proxy server itself, use it.
                if (configuration == null && this.syncConfiguration != null)
                {
                    configuration = this.syncConfiguration;
                }

                // Raise event before
                context.SyncStage = SyncStage.ConfigurationApplying;
                var beforeArgs2 = new ConfigurationApplyingEventArgs(this.ProviderTypeName, context.SyncStage);
                this.TryRaiseProgressEvent(beforeArgs2, this.ConfigurationApplying);
                bool overWriteConfiguration = beforeArgs2.OverwriteConfiguration;

                // if we have already a cache configuration, we can return, except if we should overwrite it
                if (cacheConfiguration != null && !overWriteConfiguration)
                {
                    // Raise event after
                    context.SyncStage = SyncStage.ConfigurationApplied;
                    var afterArgs2 = new ConfigurationAppliedEventArgs(this.ProviderTypeName, context.SyncStage, cacheConfiguration);
                    this.TryRaiseProgressEvent(afterArgs2, this.ConfigurationApplied);
                    // if config has been changed by user, save it again
                    this.SetCacheConfiguration(cacheConfiguration);
                    return(context, cacheConfiguration);
                }

                // create local directory
                if (!String.IsNullOrEmpty(configuration.BatchDirectory) && !Directory.Exists(configuration.BatchDirectory))
                {
                    Directory.CreateDirectory(configuration.BatchDirectory);
                }

                // if we dont have already read the tables || we want to overwrite the current config
                if ((configuration.HasTables && !configuration.HasColumns))
                {
                    string[] tables = new string[configuration.Count];

                    for (int i = 0; i < configuration.Count; i++)
                    {
                        // just check if we have a schema
                        var dmTable   = configuration[i];
                        var tableName = String.IsNullOrEmpty(dmTable.Schema) ? dmTable.TableName : $"[{dmTable.Schema}].[{dmTable.TableName}]";
                        tables[i] = tableName;
                    }

                    await this.ReadConfigurationAsync(configuration);
                }

                // save to cache
                this.SetCacheConfiguration(configuration);

                context.SyncStage = SyncStage.ConfigurationApplied;
                var afterArgs = new ConfigurationAppliedEventArgs(this.ProviderTypeName, context.SyncStage, configuration);
                this.TryRaiseProgressEvent(afterArgs, this.ConfigurationApplied);
                // if config has been changed by user, save it again
                this.SetCacheConfiguration(configuration);
                return(context, configuration);
            }
            catch (Exception ex)
            {
                if (ex is SyncException)
                {
                    throw;
                }
                else
                {
                    throw SyncException.CreateUnknowException(context.SyncStage, ex);
                }
            }
        }