Can Directory.Move fail because I am browsing the folder?

1.6k views Asked by At

I have two folder in a remote FileShare and I am trying to move the first one inside the second. To do this, I wrote a CLR that pretty much does the following:

if (Directory.Exists(destinationFolder))
{
    Directory.Delete(destinationFolder, true);
}

if (Directory.Exists(sourceFolder))
{
    Directory.Move(sourceFolder, destinationFolder);
}

This works as expected but there are some cases that am getting the following error:

System.IO.IOException: Cannot create a file when that file already exists. System.IO.IOException: at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) at System.IO.Directory.InternalMove(String sourceDirName, String destDirName, Boolean checkHost)

I was not able to narrow it down. It seems random to me since I can not reproduced it. I run this code block, over 50 times and I could not get the same error (or any error to tell the truth) as before. - Do you see anything wrong with code? - Do you have any "guesses" on what may caused this error?

The only thing I can think, is even though the Directory.Delete(destinationFolder, true); return the system does not delete the directory immediately and thus when Directory.Move(sourceFolder, destinationFolder); runs, the destinationFolder still exists.

(29/12/2016) This is not a duplicate of Cannot create a file when that file already exists when using Directory.Move. There, the user has a 'mistake' in her code and creates (Directory.CreateDirectory(destinationdirectory);) the destination folder. I am not creating the destination folder, nevertheless, I am deleting it if exists. I looked the comments and the answers but none of them gave a solution to my issue.

(30/12/2016) I have tried all the suggestions from the comments and answer but still nothing strange happens. No errors and no unexpected behaviors.

2

There are 2 answers

3
NicoRiff On

According to MSDN you´ll get that exception in the following cases:

  • An attempt was made to move a directory to a different volume.
  • destDirName already exists.
  • The sourceDirName and destDirName parameters refer to the same file or directory.
  • The directory or a file within it is being used by another process.

You´ll have to check with some of above cases, there will be the solution for sure.

https://msdn.microsoft.com/en-us/library/system.io.directory.move(v=vs.110).aspx

3
Solomon Rutzky On

The only thing I can think [of] is even though the Directory.Delete(destinationFolder, true); return the system does not delete the directory immediately and thus when Directory.Move(sourceFolder, destinationFolder); runs, the destinationFolder still exists.

I would highly doubt that this is the cause of any issue. I suppose it is not impossible, especially since this is a folder on another system (remote file share) and not local, but I would still expect any write-behind caching being done on the remote system to be completely transparent to any file system requests, not just some of them.

I think it is more likely, given the code shown in the question, that somehow you initiated two threads at nearly the exact same time and hit a race condition wherein both threads were attempting to process the move operation at the same time. You can both detect such a condition and avoid any errors by making the following changes to your code:

string _LogFile = String.Concat(@"C:\TEMP\SQLCLR_", Guid.NewGuid(), ".log");

File.AppendAllText(_LogFile, @"Starting operation for: " + sourceFolder +
                              @" --> " + destinationFolder);

if (Directory.Exists(destinationFolder))
{
    File.AppendAllText(_LogFile, @"Deleting: " + destinationFolder);
    Directory.Delete(destinationFolder, true);
}

if (Directory.Exists(sourceFolder))
{
    if (!Directory.Exists(destinationFolder))
    {
        File.AppendAllText(_LogFile, @"Moving: " + sourceFolder);
        Directory.Move(sourceFolder, destinationFolder);
    }
    else
    {
        File.AppendAllText(_LogFile, @"Oops. " + destinationFolder +
                                     @" already exists. How odd indeed!");
    }
}

This will log the operation to a text file. It will indicate exactly which steps are being taken. It will also check for the existence of the destination before calling "move", something which is not currently being checked.

If there are two competing threads, you will get 2 log files since they are named using a GUID.

If, somehow, it actually is a delayed delete issue on the remote OS, that would be indicated by a single log file containing a line for the "Deleting.." and then one for the "Moving...". OR, if the "exists" check sees the not-yet-deleted destination, then you will see a line for "Oops".