Question

So i have some code that recursively creates directories and downloads the files in the folder repectively.

Here is the code:

public class Program
    {
        static void UploadDownloadProgress(Object sender, FileDataTransferEventArgs e)
        { 
            // print a dot
            Console.WriteLine("Downloading");
            Console.WriteLine(".");
            // it's ok to go forward
            e.Cancel = false;
        } 

        public static void DownloadFolder(CloudStorage dropBoxStorage, ICloudDirectoryEntry remoteDir, string targetDir, string sourceDir)
        {           

            foreach (ICloudFileSystemEntry fsentry in remoteDir)
            {
                var filepath = Path.Combine(targetDir, fsentry.Name);

                if (fsentry is ICloudDirectoryEntry)
                {
                    Console.WriteLine("Created: {0}", filepath);
                    Directory.CreateDirectory(filepath);
                    DownloadFolder(dropBoxStorage, fsentry as ICloudDirectoryEntry, filepath, sourceDir);
                }
                else
                {
                    dropBoxStorage.DownloadFile(remoteDir, fsentry.Name, targetDir, UploadDownloadProgress);
                }
            }
        }

        static void Main(string[] args)
        {
            CloudStorage dropBoxStorage = new CloudStorage();

            var dropBixConfig = CloudStorage.GetCloudConfigurationEasy(nSupportedCloudConfigurations.DropBox);

            ICloudStorageAccessToken accessToken = null;

            using (FileStream fs = File.Open(@"C:\Users\Michael\token.txt", FileMode.Open, FileAccess.Read, FileShare.None))
            {
                accessToken = dropBoxStorage.DeserializeSecurityToken(fs);
            }

            var storageToken = dropBoxStorage.Open(dropBixConfig, accessToken);

            //do stuff
            //var root = dropBoxStorage.GetRoot();

            var publicFolder = dropBoxStorage.GetFolder("/Public");

            foreach (var folder in publicFolder)
            {
                Boolean bIsDirectory = folder is ICloudDirectoryEntry;

                Console.WriteLine("{0}: {1}", bIsDirectory ? "DIR" : "FIL", folder.Name);
            }

            string remoteDirName = @"/Public/IQSWS";
            string targetDir = @"C:\Users\Michael\";
            var remoteDir = dropBoxStorage.GetFolder(remoteDirName);

            DownloadFolder(dropBoxStorage, remoteDir, targetDir, remoteDirName);

            dropBoxStorage.Close();
        }

        public delegate void FileOperationProgressChanged(object sender, FileDataTransferEventArgs e);    
    }

After the loop finds the first file in the else, it downloads it and then moves on to the next file, however it throws an exception on the foreach:

System.InvalidOperationException was unhandled
  HResult=-2146233079
  Message=Collection was modified; enumeration operation may not execute.
  Source=mscorlib
  StackTrace:
       at System.Collections.Generic.Dictionary`2.ValueCollection.Enumerator.MoveNext()
       at StartServer.Program.DownloadFolder(CloudStorage dropBoxStorage, ICloudDirectoryEntry remoteDir, String targetDir, String sourceDir) in c:\Users\Michael\Documents\Visual Studio 2012\Projects\IQS Source\StartServer\StartServer\Program.cs:line 29
       at StartServer.Program.DownloadFolder(CloudStorage dropBoxStorage, ICloudDirectoryEntry remoteDir, String targetDir, String sourceDir) in c:\Users\Michael\Documents\Visual Studio 2012\Projects\IQS Source\StartServer\StartServer\Program.cs:line 39
       at StartServer.Program.DownloadFolder(CloudStorage dropBoxStorage, ICloudDirectoryEntry remoteDir, String targetDir, String sourceDir) in c:\Users\Michael\Documents\Visual Studio 2012\Projects\IQS Source\StartServer\StartServer\Program.cs:line 39
       at StartServer.Program.DownloadFolder(CloudStorage dropBoxStorage, ICloudDirectoryEntry remoteDir, String targetDir, String sourceDir) in c:\Users\Michael\Documents\Visual Studio 2012\Projects\IQS Source\StartServer\StartServer\Program.cs:line 39
       at StartServer.Program.Main(String[] args) in c:\Users\Michael\Documents\Visual Studio 2012\Projects\IQS Source\StartServer\StartServer\Program.cs:line 80
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: 

Why is this happening? Why is the array edited when all I do is download a file?

Was it helpful?

Solution

You're passing the whole collection remoteDir into the DownloadFile method, which looks like this:

// AppLimit.CloudComputing.SharpBox.CloudStorage
public void DownloadFile(ICloudDirectoryEntry parent, string name, string targetPath, FileOperationProgressChanged delProgress)
{
    if (parent == null || name == null || targetPath == null)
    {
        throw new SharpBoxException(SharpBoxErrorCodes.ErrorInvalidParameters);
    }
    targetPath = Environment.ExpandEnvironmentVariables(targetPath);
    ICloudFileSystemEntry child = parent.GetChild(name);
    using (FileStream fileStream = new FileStream(Path.Combine(targetPath, name), FileMode.Create, FileAccess.Write, FileShare.None))
    {
        child.GetDataTransferAccessor().Transfer(fileStream, nTransferDirection.nDownload, delProgress, null);
    }
}

The only point at which parent could be modified in this method seems to be where it calls GetChild. Looking at GetChild (I used IlSpy):

public ICloudFileSystemEntry GetChild(string name)
{
  return this.GetChild(name, true);
}

public ICloudFileSystemEntry GetChild(string name, bool bThrowException)
{
  this.RefreshResource();
  ICloudFileSystemEntry cloudFileSystemEntry;
  this._subDirectories.TryGetValue(name, out cloudFileSystemEntry);
  if (cloudFileSystemEntry == null && bThrowException)
  {
    throw new SharpBoxException(SharpBoxErrorCodes.ErrorFileNotFound);
  }
  return cloudFileSystemEntry;
}

This, in turn, calls RefreshResource on the collection. Which looks like this (have assumed the dropbox implementation):

// AppLimit.CloudComputing.SharpBox.StorageProvider.BaseObjects.BaseDirectoryEntry
private void RefreshResource()
{
    this._service.RefreshResource(this._session, this);
    this._subDirectoriesRefreshedInitially = true;
}


// AppLimit.CloudComputing.SharpBox.StorageProvider.DropBox.Logic.DropBoxStorageProviderService
public override void RefreshResource(IStorageProviderSession session, ICloudFileSystemEntry resource)
{
    string resourceUrlInternal = this.GetResourceUrlInternal(session, resource);
    int num;
    string text = DropBoxRequestParser.RequestResourceByUrl(resourceUrlInternal, this, session, out num);
    if (text.Length == 0)
    {
        throw new SharpBoxException(SharpBoxErrorCodes.ErrorCouldNotRetrieveDirectoryList);
    }
    DropBoxRequestParser.UpdateObjectFromJsonString(text, resource as BaseFileEntry, this, session);
}

Now, this then calls UpdateObjectFromJsonString (which I'm not going to paste here because it's huge), but this then seems to update the resource object which belongs to the collection. Hence your collection gets changed... and your exception.

I recommend you download the source to SharpBox or use IlSpy to disassemble the binaries if you're interested in what exactly is happening. But in short, if you download the file in this way it's going to modify the collection.

Maybe use one of the other DownloadFile overloads which doesn't pass in the whole collection.

OTHER TIPS

Not sure if the problem has been solved or not, but from my experience, collection modified needs you to cast IEnumerable object to ToList() so that it doesn't load eagerly.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top