title: Getting a System.Error when attempting to Sync using batching in Sync Framework
body: <p>I am writing an n-tiered application that uses the Sync Framework v2.1 in combination with a WCF web service. My code is based on this example: <a href="http://code.msdn.microsoft.com/Database-Sync-SQL-Server-7e88adab" rel="nofollow">Database-Sync</a></p>
  
  <p>Syncing seems to work without a problem. But, when I add the batching functionality I get an error that is proving difficult to debug. </p>
  
  <p>In the client portion of the solution (a WPF app), I have a virtual class that inherits from KnowledgeSyncProvider. It also implements the IDisposable interface. I use this class to call the WCF service I wrote and use the sync functionality there.  When the sync code runs (orchestrator.Synchronize() is called), everything seems to work correctly and the EndSession function override that I have written runs without error. But, after execution leaves that function, a System.Error occurs from within Microsoft.Synchronization.CoreInterop.ISyncSession.Start (as far as I can tell). </p>
  
  <p>The functions I have written to provide the IDisposable functionality are never called so, something is happening after EndSession, but before my application's KnowledgeSyncProvider virtual class can be disposed. </p>
  
  <p>Here is the error information:</p>
  
  <p>_message: "System error."
  Stack Trace:    at Microsoft.Synchronization.CoreInterop.ISyncSession.Start(CONFLICT_RESOLUTION_POLICY resolutionPolicy, _SYNC_SESSION_STATISTICS&amp; pSyncSessionStatistics)
     at Microsoft.Synchronization.KnowledgeSyncOrchestrator.DoOneWaySyncHelper(SyncIdFormatGroup sourceIdFormats, SyncIdFormatGroup destinationIdFormats, KnowledgeSyncProviderConfiguration destinationConfiguration, SyncCallbacks DestinationCallbacks, ISyncProvider sourceProxy, ISyncProvider destinationProxy, ChangeDataAdapter callbackChangeDataAdapter, SyncDataConverter conflictDataConverter, Int32&amp; changesApplied, Int32&amp; changesFailed)
     at Microsoft.Synchronization.KnowledgeSyncOrchestrator.DoOneWayKnowledgeSync(SyncDataConverter sourceConverter, SyncDataConverter destinationConverter, SyncProvider sourceProvider, SyncProvider destinationProvider, Int32&amp; changesApplied, Int32&amp; changesFailed)
     at Microsoft.Synchronization.KnowledgeSyncOrchestrator.Synchronize()
     at Microsoft.Synchronization.SyncOrchestrator.Synchronize()
     at MyApplication.Library.SynchronizationHelper.SynchronizeProviders() in C:\My Documents\Visual Studio 2010\Projects\MyApplication\MyApplication\Library\SynchronizationHelper.cs:line 43
     at MyApplication.ViewModels.MainViewModel.SynchronizeDatabases() in C:\My Documents\Visual Studio 2010\Projects\MyApplication\MyApplication\ViewModels\MainViewModel.cs:line 46</p>
  
  <p>I have enabled trace info but, that doesn't seem to provide any helpful information in this case.</p>
  
  <p>Can anyone make a suggestion as to how I might be able to figure out what is causing this error?</p>
  
  <p>Thanks in advance for any help you can provide.</p>
  
  <p>Here is the code that handles syncing:</p>
  
  <pre><code>public void SynchronizeProviders()
  </code></pre>
  
  <p>{
          CeDatabase localDb = new CeDatabase();
          localDb.Location = Directory.GetCurrentDirectory() + "\Data\SYNCTESTING5.sdf";
          RelationalSyncProvider localProvider = ConfigureCeSyncProvider(localDb.Connection);
          MyAppKnowledgeSyncProvider srcProvider = new MyAppKnowledgeSyncProvider();
          localProvider.MemoryDataCacheSize = 5000;
          localProvider.BatchingDirectory = "c:\batchingdir";
      srcProvider.BatchingDirectory = "c:\batchingdir";</p>
  
  <pre><code>SyncOrchestrator orchestrator = new SyncOrchestrator();
  orchestrator.LocalProvider = localProvider;
  orchestrator.RemoteProvider = srcProvider;
  orchestrator.Direction = SyncDirectionOrder.UploadAndDownload;
  
  CheckIfProviderNeedsSchema(localProvider as SqlCeSyncProvider, srcProvider);
  SyncOperationStatistics stats = orchestrator.Synchronize();
  </code></pre>
  
  <p>}</p>
  
  <p>And here is the KnowledgeSyncProviderClass that is used to create srcProvider:</p>
  
  <pre><code>   class MyAppKnowledgeSyncProvider : KnowledgeSyncProvider, IDisposable
  {
      protected MyAppSqlSync.IMyAppSqlSync proxy;
      protected SyncIdFormatGroup idFormatGroup;
      protected DirectoryInfo localBatchingDirectory;
      protected bool disposed = false;
  
      private string batchingDirectory = Environment.ExpandEnvironmentVariables("%TEMP%");
  
      public string BatchingDirectory
      {
          get { return batchingDirectory; }
          set 
          {
              if (string.IsNullOrEmpty(value))
              {
                  throw new ArgumentException("value cannot be null or empty");
              }
              try
              {
                  Uri uri = new Uri(value);
                  if (!uri.IsFile || uri.IsUnc)
                  {
                      throw new ArgumentException("value must be a local directory");
                  }
                  batchingDirectory = value;
              }
              catch (Exception e)
              {
                  throw new ArgumentException("Invalid batching directory.", e);
              }
          }
      }
  
      public MyAppKnowledgeSyncProvider()
      {
          this.proxy = new MyAppSqlSync.MyAppSqlSyncClient();
          this.proxy.Initialize();
      }
  
      public override void BeginSession(SyncProviderPosition position, SyncSessionContext syncSessionContext)
      {
          this.proxy.BeginSession(position);
      }
  
      public DbSyncScopeDescription GetScopeDescription()
      {
          return this.proxy.GetScopeDescription();
      }
  
      public override void EndSession(SyncSessionContext syncSessionContext)
      {
          this.proxy.EndSession();
          if (this.localBatchingDirectory != null)
          {
              this.localBatchingDirectory.Refresh();
  
              if (this.localBatchingDirectory.Exists)
              {
                  this.localBatchingDirectory.Delete(true);
              }
          }          
      }
  
      public override ChangeBatch GetChangeBatch(uint batchSize, SyncKnowledge destinationKnowledge, out object changeDataRetriever)
      {
          MyAppSqlSync.GetChangesParameters changesWrapper = proxy.GetChanges(batchSize, destinationKnowledge);
          changeDataRetriever = changesWrapper.DataRetriever;
  
          DbSyncContext context = changeDataRetriever as DbSyncContext;
          if (context != null &amp;&amp; context.IsDataBatched)
          {
              if (this.localBatchingDirectory == null)
              {
                  string remotePeerId = context.MadeWithKnowledge.ReplicaId.ToString();
                  string sessionDir = Path.Combine(this.batchingDirectory, "MyAppSync_" + remotePeerId);
                  this.localBatchingDirectory = new DirectoryInfo(sessionDir);
                  if (!this.localBatchingDirectory.Exists)
                  {
                      this.localBatchingDirectory.Create();
                  }
              }
  
              string localFileName = Path.Combine(this.localBatchingDirectory.FullName, context.BatchFileName);
              FileInfo localFileInfo = new FileInfo(localFileName);
  
              if (!localFileInfo.Exists)
              {
                  byte[] remoteFileContents = this.proxy.DownloadBatchFile(context.BatchFileName);
                  using (FileStream localFileStream = new FileStream(localFileName, FileMode.Create, FileAccess.Write))
                  {
                      localFileStream.Write(remoteFileContents, 0, remoteFileContents.Length);
                  }
              }
              context.BatchFileName = localFileName;
          }
          return changesWrapper.ChangeBatch;
      }
  
      public override FullEnumerationChangeBatch GetFullEnumerationChangeBatch(uint batchSize, SyncId lowerEnumerationBound, SyncKnowledge knowledgeForDataRetrieval, out object changeDataRetriever)
      {
          throw new NotImplementedException();
      }
  
      public override void GetSyncBatchParameters(out uint batchSize, out SyncKnowledge knowledge)
      {
          MyAppSqlSync.SyncBatchParameters wrapper = proxy.GetKnowledge();
          batchSize = wrapper.BatchSize;
          knowledge = wrapper.DestinationKnowledge;
      }
  
      public override SyncIdFormatGroup IdFormats
      {
          get
          {
              if (idFormatGroup == null)
              {
                  idFormatGroup = new SyncIdFormatGroup();
  
                  idFormatGroup.ChangeUnitIdFormat.IsVariableLength = false;
                  idFormatGroup.ChangeUnitIdFormat.Length = 1;
  
                  idFormatGroup.ReplicaIdFormat.IsVariableLength = false;
                  idFormatGroup.ReplicaIdFormat.Length = 16;
  
                  idFormatGroup.ItemIdFormat.IsVariableLength = true;
                  idFormatGroup.ItemIdFormat.Length = 10 * 1024;
              }
              return idFormatGroup;
          }
      }
  
      public override void ProcessChangeBatch(ConflictResolutionPolicy resolutionPolicy, ChangeBatch sourceChanges, object changeDataRetriever, SyncCallbacks syncCallbacks, SyncSessionStatistics sessionStatistics)
      {
          DbSyncContext context = changeDataRetriever as DbSyncContext;
          if (context != null &amp;&amp; context.IsDataBatched)
          {
              string fileName = new FileInfo(context.BatchFileName).Name;
              string peerId = context.MadeWithKnowledge.ReplicaId.ToString();
              if (!this.proxy.HasUploadedBatchFile(fileName, peerId))
              {
                  FileStream stream = new FileStream(context.BatchFileName, FileMode.Open, FileAccess.Read);
                  byte[] contents = new byte[stream.Length];
                  using (stream)
                  {
                      stream.Read(contents, 0, contents.Length);
                  }
                  this.proxy.UploadBatchFile(fileName, contents, peerId);
              }
  
              context.BatchFileName = fileName;
          }
          SyncSessionStatistics stats = this.proxy.ApplyChanges(resolutionPolicy, sourceChanges, changeDataRetriever);
          sessionStatistics.ChangesApplied += stats.ChangesApplied;
          sessionStatistics.ChangesFailed += stats.ChangesFailed;
      }
  
      public override void ProcessFullEnumerationChangeBatch(ConflictResolutionPolicy resolutionPolicy, FullEnumerationChangeBatch sourceChanges, object changeDataRetriever, SyncCallbacks syncCallbacks, SyncSessionStatistics sessionStatistics)
      {
          throw new NotImplementedException();
      }
  
      ~MyAppKnowledgeSyncProvider()
      {
          Dispose(false);
      }
  
      #region IDisposable Members
  
      public virtual void Dispose()
      {
          Dispose(true);
          GC.SuppressFinalize(this);
      }
  
      protected virtual void Dispose(bool disposing)
      {
          if (!this.disposed)
          {
              if (disposing)
              {
                  if (this.proxy != null)
                  {
                      CloseProxy();
                  }
              }
  
              disposed = true;
          }
      }
  
      public virtual void CloseProxy()
      {
          if (this.proxy != null)
          {
              this.proxy.Cleanup();
              ICommunicationObject channel = proxy as ICommunicationObject;
              if (channel != null)
              {
                  try
                  {
                      channel.Close();
                  }
                  catch (TimeoutException)
                  {
                      channel.Abort();
                  }
                  catch (CommunicationException)
                  {
                      channel.Abort();
                  }
              }
              this.proxy = null;
          }
      }
  
      #endregion
  }
  </code></pre>