locked
Multiple scope problem - SQLExpress2008 - SQLCe RRS feed

  • Question

  • Hello,
      I am trying to sync a SQLExpress 2008 and a SQLCE db with 2 scopes. One scope sends the changes from 5 tables to the CE db amd the other scope sends changes from a sixth table both ways. Here is how I am provision the DB's (I started with the code in the MSDN example http://code.msdn.microsoft.com/Release/ProjectReleases.aspx?ProjectName=sync&ReleaseId=3423 :

      public SqlSyncProvider ConfigureSqlSyncProvider(string hostName)
    
      {
    
       SyncServerSQL svrConnString = new SyncServerSQL();
    
       SqlSyncProvider provider = new SqlSyncProvider();
    
       provider.ScopeName = SyncUtils.ScopeNameToClient;
    
       provider.Connection = new SqlConnection(svrConnString.SqlConnString);
    
       
    
       DbSyncScopeDescription scopeDesc = new DbSyncScopeDescription("TestToClient");
    
       SqlSyncScopeProvisioning serverConfig = new SqlSyncScopeProvisioning((System.Data.SqlClient.SqlConnection)provider.Connection);
    
    
    
       if (!serverConfig.ScopeExists("TestToClient"))
    
       {
    
        scopeDesc.Tables.Add(SqlSyncDescriptionBuilder.GetDescriptionForTable("Table_001", (System.Data.SqlClient.SqlConnection)provider.Connection));
    
        scopeDesc.Tables.Add(SqlSyncDescriptionBuilder.GetDescriptionForTable("Table_002", (System.Data.SqlClient.SqlConnection)provider.Connection));
    
        scopeDesc.Tables.Add(SqlSyncDescriptionBuilder.GetDescriptionForTable("Table_003", (System.Data.SqlClient.SqlConnection)provider.Connection));
    
        scopeDesc.Tables.Add(SqlSyncDescriptionBuilder.GetDescriptionForTable("Table_004", (System.Data.SqlClient.SqlConnection)provider.Connection));
    
        scopeDesc.Tables.Add(SqlSyncDescriptionBuilder.GetDescriptionForTable("Table_005", (System.Data.SqlClient.SqlConnection)provider.Connection));
    
        serverConfig.PopulateFromScopeDescription(scopeDesc);
    
        serverConfig.SetCreateTableDefault(DbSyncCreationOption.Skip);
    
        serverConfig.Apply();
    
       }
    
       DbSyncScopeDescription scopeDescBothWays = new DbSyncScopeDescription("TestBothWays");
    
       SqlSyncScopeProvisioning serverConfigBothWays = new SqlSyncScopeProvisioning((System.Data.SqlClient.SqlConnection)provider.Connection);
    
       if (!serverConfigBothWays.ScopeExists("TestBothWays"))
    
       {
    
        scopeDescBothWays.Tables.Add(SqlSyncDescriptionBuilder.GetDescriptionForTable("Table_006", (System.Data.SqlClient.SqlConnection)provider.Connection));
    
        serverConfigBothWays.PopulateFromScopeDescription(scopeDescBothWays);
    
        serverConfigBothWays.SetCreateTableDefault(DbSyncCreationOption.Skip);
    
        serverConfigBothWays.Apply();
    
       }
    
       provider.BatchApplied += new EventHandler<DbBatchAppliedEventArgs>(provider_BatchApplied);
    
       provider.BatchSpooled += new EventHandler<DbBatchSpooledEventArgs>(provider_BatchSpooled);
    
       return provider;
    
      }
    
      public SqlCeSyncProvider ConfigureCESyncProvider(SqlCeConnection sqlCeConnection)
    
      {
    
       SqlCeSyncProvider provider = new SqlCeSyncProvider();
    
       provider.ScopeName = SyncUtils.ScopeNameToClient;
    
       provider.Connection = sqlCeConnection;
    
       SyncServerSQL svrConnString = new SyncServerSQL();
    
       SqlConnection serverConn = new SqlConnection(svrConnString.SqlConnString);
    
       DbSyncScopeDescription scopeDesc = SqlSyncDescriptionBuilder.GetDescriptionForScope(SyncUtils.ScopeNameToClient, serverConn);
    
       SqlCeSyncScopeProvisioning serverConfig = new SqlCeSyncScopeProvisioning(sqlCeConnection,scopeDesc);
    
       DbSyncScopeDescription scopeDescBothWays = SqlSyncDescriptionBuilder.GetDescriptionForScope(SyncUtils.ScopeNameBothWays, serverConn);
    
       SqlCeSyncScopeProvisioning serverConfigBothWays = new SqlCeSyncScopeProvisioning(sqlCeConnection, scopeDescBothWays);
    
       if (!serverConfig.ScopeExists(SyncUtils.ScopeNameToClient) || !serverConfigBothWays.ScopeExists(SyncUtils.ScopeNameBothWays) || SyncUtils.ScopeRebuild)
    
       { 
    
        SqlCeSyncScopeDeprovisioning clientSqlCeDepro = new SqlCeSyncScopeDeprovisioning(sqlCeConnection);
    
        try
    
         {
    
         clientSqlCeDepro.DeprovisionStore();
    
        }
    
        catch
    
        {
    
        }
    
        serverConfig.Apply();
    
        serverConfigBothWays.Apply();
    
       }
    
       provider.BeginSnapshotInitialization += new EventHandler<DbBeginSnapshotInitializationEventArgs>(provider_BeginSnapshotInitialization);
    
       provider.EndSnapshotInitialization += new EventHandler<DbEndSnapshotInitializationEventArgs>(provider_EndSnapshotInitialization);
    
       provider.BatchApplied += new EventHandler<DbBatchAppliedEventArgs>(provider_BatchApplied);
    
       provider.BatchSpooled += new EventHandler<DbBatchSpooledEventArgs>(provider_BatchSpooled);
    
       return provider;
    
      }
    
    

    The syncronzation to the client works, but when I try to to the 2-way syncronization I get the following error:
    Cannot apply changes because the local provider does not have adapters configured for the following tables that were received from the remote provider: Table_001, Table_002, Table_003, Table_004, Table_005. Ensure that the correct adapters have been added to both providers for Scope 'Test_BothWays', and that any table mapping has been correctly configured.
    If I add Tables 001...005 to the 2-way scope, I get the same error but only for Table_006
    If I then add Table_006 to the client only scope, both synconizations run without error.

    Can anyone point out if I am missing something obvious in trying to make the 2 scopes work?

    Thanks in advance.
    Hank

     


    Monday, March 21, 2011 2:34 PM

Answers

  • your ConfigureSqlSyncProvider and ConfigureCESyncProvider are provisioning two scopes, however, you are setting the provider.ScopeName to ScopeNameToClient. So when they get added to the collection, their scopenames are both ScopeNameToClient.

     

    • Marked as answer by HankAnzis Monday, March 21, 2011 4:56 PM
    Monday, March 21, 2011 4:16 PM

All replies

  • can you post the code snippet for the part you're synchronizing?
    Monday, March 21, 2011 3:04 PM
  • Sure,

    Here is the code to do the synchronization in both directions

        private void doSynchronizeBothWays()
        {
    
          RelationalSyncProvider srcProvider = providersCollection["Server"];
          RelationalSyncProvider destinationProvider = providersCollection["Client 1"];
          SyncDirectionOrder determinedSyncDirection = SyncDirectionOrder.UploadAndDownload; //(destinationProvider is SqlSyncProvider) ? SyncDirectionOrder.UploadAndDownload : SyncDirectionOrder.DownloadAndUpload;
    
          RelationalProviderProxy destinationProxy = null;
          string hostName = (destinationProvider is SqlSyncProvider)
            ? ((SqlConnection)destinationProvider.Connection).DataSource
            : destinationProvider.Connection.Database;
    
          if (destinationProvider is SqlSyncProvider)
          {
            destinationProxy = new SqlSyncProviderProxy(SyncUtils.ScopeNameBothWays, ((SqlConnection)destinationProvider.Connection).DataSource);
          }
          else
          {
            //create a reference to the server proxy
            SqlSyncProviderProxy serverProxy = new SqlSyncProviderProxy(SyncUtils.ScopeNameBothWays, SyncUtils.ScopeMachine);
    
            //retrieve the scope description from the server
            DbSyncScopeDescription scopeDesc = serverProxy.GetScopeDescriptionBothWays();
    
            serverProxy.Dispose();
    
            //generate a new proxy for the client
            destinationProxy = new CeSyncProviderProxy(SyncUtils.ScopeNameBothWays, destinationProvider.Connection.Database, scopeDesc);
          }
    
          //Set memory data cache size property. 0 represents non batched mode
          srcProvider.MemoryDataCacheSize = this._batchSize;
    
          //No need to set memory cache size for Proxy as since the source is enabled for batching, both upload and download will
          //be batched. 
    
          //Set batch spool location. Default value if not set is %Temp% directory.
          if (!string.IsNullOrEmpty(batchSpoolLocation))
          {
            srcProvider.BatchingDirectory = batchSpoolLocation;
            destinationProxy.BatchingDirectory = batchSpoolLocation;
          }
    
          SyncOperationStatistics stats = synchronizationHelper.SynchronizeProviders(srcProvider, destinationProxy, determinedSyncDirection);
    
          TimeSpan diff = stats.SyncEndTime.Subtract(stats.SyncStartTime);
    
          destinationProxy.Dispose();
    
          //Print Sync stats object
          Console.WriteLine(string.Format("Batching: {4} - Total Time To Synchronize = {0}:{1}:{2}:{3}",
            diff.Hours, diff.Minutes, diff.Seconds, diff.Milliseconds, (this._batchSize > 0) ? "Enabled" : "Disabled"));
        }
    
      }
    
    
    and here is the code block that is giving the error
    
        public SyncSessionStatistics ApplyChanges(ConflictResolutionPolicy resolutionPolicy, ChangeBatch sourceChanges, object changeData)
        {
          Log("ProcessChangeBatch: {0}", this.peerProvider.Connection.ConnectionString);
    
          DbSyncContext dataRetriever = changeData as DbSyncContext;
    
          if (dataRetriever != null && dataRetriever.IsDataBatched)
          {
            string remotePeerId = dataRetriever.MadeWithKnowledge.ReplicaId.ToString();
            //Data is batched. The client should have uploaded this file to us prior to calling ApplyChanges.
            //So look for it.
            //The Id would be the DbSyncContext.BatchFileName which is just the batch file name without the complete path
            string localBatchFileName = null;
            if (!this.batchIdToFileMapper.TryGetValue(dataRetriever.BatchFileName, out localBatchFileName))
            {
              //Service has not received this file. Throw exception
              throw new FaultException<WebSyncFaultException>(new WebSyncFaultException("No batch file uploaded for id " + dataRetriever.BatchFileName, null));
            }
            dataRetriever.BatchFileName = localBatchFileName;
          }
    
          SyncSessionStatistics sessionStatistics = new SyncSessionStatistics();
    <strong>      this.peerProvider.ProcessChangeBatch(resolutionPolicy, sourceChanges, changeData, new SyncCallbacks(), sessionStatistics);
    </strong>      return sessionStatistics;
        }
    

     

    Thanks for looking at this. Let me know if I'm not sending the right code.

    Hank

    Monday, March 21, 2011 3:18 PM
  • your ConfigureSqlSyncProvider and ConfigureCESyncProvider are provisioning two scopes, however, you are setting the provider.ScopeName to ScopeNameToClient. So when they get added to the collection, their scopenames are both ScopeNameToClient.

     

    • Marked as answer by HankAnzis Monday, March 21, 2011 4:56 PM
    Monday, March 21, 2011 4:16 PM
  • nope. a provider has only one ScopeName property. you should pass the scope name you want to sync in the ConfigureSqlSyncProvider and ConfigureCESyncProvider and assign it to the Scopename property.
    Monday, March 21, 2011 4:24 PM
  • I see what you are telling me now. I need to have a separate provider for each scope and call the ones I need for each separate sync.

    Thanks JuneT,

    Hank

    Monday, March 21, 2011 4:56 PM