×
您的位置: 首页 > 编程笔记

使用Directory.Delete()和Directory.CreateDirectory()来覆盖文件夹(Using Directory.Delete() and Directory.Create

ASP.NET 时间:2020-06-19  查看:0   收藏

In my WebApi action method, I want to create/over-write a folder using this code:

string myDir = "...";
if(Directory.Exists(myDir)) 
{
    Directory.Delete(myDir, true);
}
Directory.CreateDirectory(myDir);

// 1 - Check the dir 
Debug.WriteLine("Double check if the Dir is created: " + Directory.Exists(myDir));

// Some other stuff here...

// 2 - Check the dir again
Debug.WriteLine("Check again if the Dir still exists: " + Directory.Exists(myDir));

Issue

Strangely, sometimes right after creating the directory, the directory does not exist!

Sometimes when checking the dir for the first time (where the number 1 is); Directory.Exist() returns true, other times false. Same happens when checking the dir for the second time (where the number 2 is).

Notes

  • None of this part of code throw any exception.

  • Only can reproduce this when publishing the website on server. (Windows server 2008)

  • Happens when accessing the same folder.

Questions

  • Is this a concurrency issue race condition?

  • Doesn't WebApi or the Operating System handle the concurrency?

  • Is this the correct way to overwrite a folder?

  • Should I lock files manually when we have many API requests to the same file?

Or in General:

  • What's the reason for this strange behavior?

UPDATE:

  • Using DirectoryInfo and Refresh() instead of Directory does not solve the problem.

  • Only happens when the recursive option of Delete is true. (and the directory is not empty).

解决方案

Many filesystem operations are not synchonous on some filesystems (in case of windows - NTFS). Take for example RemoveDirectory call (which is called by Directory.DeleteDirectory at some point):

The RemoveDirectory function marks a directory for deletion on close. Therefore, the directory is not removed until the last handle to the directory is closed.

As you see, it will not really delete directory until all handles to it are closed, but Directory.DeleteDirectory will complete fine. In your case that is also most likely such concurrency problem - directory is not really created while you executing Directory.Exists.

So, just periodically check what you need and don't consider filesystem calls in .NET to be synchronous. You can also use FileSystemWatcher in some cases to avoid polling.

EDIT: I was thinking how to reproduce it, and here is the code:

internal class Program {
    private static void Main(string[] args) {
        const string path = "G:\test_dir";
        while (true) {         
            if (Directory.Exists(path))
                Directory.Delete(path);       
            Directory.CreateDirectory(path);   
            if (!Directory.Exists(path))
                throw new Exception("Confirmed");                 
        }            
    }        
}

You see that if all filesystem calls were synchronous (in .NET), this code should run without problem. Now, before running that code, create empty directory at specified path (preferrably don't use SSD for that) and open it with windows explorer. Now run the code. For me it either throws Confirmed (which exactly reproduces your issue) or throws on Directory.Delete saying that directory does not exist (almost the same case). It does it 100% of the time for me.

Here is another code which when running on my machine confirms that it's certainly possible for File.Exists to return true directly after File.Delete call:

internal class Program {
    private static void Main(string[] args) {
        while (true) {
            const string path = @"G:test_dirtest.txt";
            if (File.Exists(path))
                File.Delete(path);
            if (File.Exists(path))
                throw new Exception("Confirmed");
            File.Create(path).Dispose();
        }
    }        
 }

exception

To do this, I opened G:test_dir folder and during execution of this code tried to open constantly appearing and disappearing test.txt file. After couple of tries, Confirmed exception was thrown (while I didn't create or delete that file, and after exception is thrown, it's not present on filesystem already).  So race conditions are possible in multiple cases and my answer is correct one.

在我的的WebAPI 的操作方法,我想创建/覆写使用该code文件夹:

 字符串MYDIR =...;
如果(Directory.Exists(MYDIR))
{
    Directory.Delete(MYDIR,真);
}
Directory.CreateDirectory(MYDIR);// 1  - 检查目录
的Debug.WriteLine(如果目录中创建仔细检查:+ Directory.Exists(MYDIR));//此处是一些其他的东西...// 2  - 再次检查目录
的Debug.WriteLine(再次检查目录仍然存在:+ Directory.Exists(MYDIR));

问题

奇怪的是,有时创建目录之后,该目录不存在!

有时检查首次迪尔(其中数字1是)时;  Directory.Exist()收益真正,其他时间假 。检查第二次(其中,数字2是)的目录时发生同样的情况。

备注


  • code,这部分没有抛出任何异常。


  • 发布在服务器上的网站时,只可以重现此。 (Windows Server 2008中)


  • 访问同一个文件夹时,会发生。


问题


  • 这是一个并发的问题竞争条件吗?


  • 不的WebAPI 或操作系统处理并发?


  • 这是覆盖一个文件夹?

  • 的正确方法


  • 我应该手动锁定文件时,我们有很多的API请求到同一个文件?


或一般的:


  • 什么是这个奇怪的行为的原因是什么?


更新:


  • 使用的DirectoryInfo 和刷新()而不是目录并不能解决这个问题。


  • 只有发生时的的递归的选项的删除的是真正。 (和目录不为空)。



解决方案

许多文件系统操作都没有对一些文件系统synchonous(在Windows的情况下, -  NTFS)。举个例子 RemoveDirectory 调用(被称为通过在某些点Directory.DeleteDirectory):


  

该RemoveDirectory功能,标志着接近上删除一个目录。因此,目录不会被删除,直到最后句柄目录被关闭。


块引用>

正如你看到的,它不会真正删除目录,直到所有的处理将其关闭,但Directory.DeleteDirectory将完成罚款。你的情况,这也是最有可能这样的并发问题 - 当你执行Directory.Exists目录是不是真的创建。

所以,只要定期检查您需要什么,不考虑在.NET中的文件系统调用是同步的。也可以使用FileSystemWatcher的在某些情况下,以避免轮询

编辑:我在想如何重现它,这里是code:

 内部类节目{
    私有静态无效的主要(字串[] args){
        常量字符串路径=G:\\ test_dir
        而(真){
            如果(Directory.Exists(路径))
                Directory.Delete(路径);
            Directory.CreateDirectory(路径);
            如果(!Directory.Exists(路径))
                抛出新的异常(确认);
        }
    }
}

您看,如果所有文件系统的电话都是同步的(.NET中),这code应该没有问题运行。现在,运行code之前,在创建指定路径空目录(preferrably不要使用SSD为),并与Windows资源管理器打开它。现在运行code。对我来说,无论是罚球确认(这正是再现您的问题),或者抛出的Directory.Delete说,目录不存在(几乎相同的情况下)。它做它的100%的时间我。

下面是我的机器上运行时,另一code证实,的确可以为File.Exists到File.Delete调用后直接返回true:

 内部类节目{
    私有静态无效的主要(字串[] args){
        而(真){
            常量字符串路径= @G:\ test_dir \ test.txt的;
            如果(File.Exists(路径))
                File.Delete(路径);
            如果(File.Exists(路径))
                抛出新的异常(确认);
            File.Create(路径).Dispose();
        }
    }
 }

例外

要做到这一点,我开G:\ test_dir文件夹,这个code的执行过程中试图打开不断出现和消失test.txt文件。几次尝试后,确认异常被抛出(虽然我没有创建或删除文件,并抛出异常后,它不是文件系统present的话)。所以比赛条件在多种情况是可能的,我的答案是正确的。

 

0% (0)
0% (0)