在C#中对文件类使用静态方法安全吗?

在C#中对文件类使用静态方法安全吗?,c#,asp.net,file-io,C#,Asp.net,File Io,我在ASP.Net应用程序的代码隐藏中有以下代码,在该应用程序中,先读取一个文件,然后再写入该文件 代码 var state= File.ReadAllText(Server.MapPath(string.Format("~/state/{0}", fileName))); if(state.indexOf("1") == 0) { File.WriteAllText(Server.MapPath(string.Format("~/state/{0}", fileName)), newSt

我在ASP.Net应用程序的代码隐藏中有以下代码,在该应用程序中,先读取一个文件,然后再写入该文件

代码

var state= File.ReadAllText(Server.MapPath(string.Format("~/state/{0}", fileName)));
if(state.indexOf("1") == 0) 
{
  File.WriteAllText(Server.MapPath(string.Format("~/state/{0}", fileName)), newState);
}
有时,但并非总是,我得到以下例外

例外情况

进程无法访问文件“C:\inetpub\wwwroot\mywebsite1\state\20150905005929435\u edf9267e-fad1-45a7-bfe2-0e6e643798b5”,因为另一进程正在使用该文件。

我猜文件读取操作有时在写入操作发生之前没有关闭文件,或者可能是文件写入操作在web应用程序发出下一个请求之前没有关闭文件。但是,我找不到确切的原因

问题:如何避免发生此错误?使用
File
类,而使用传统的FileStream对象方法(我总是显式地处理FileStream对象),这样不安全吗

更新1

我尝试了一种重试循环方法,但即使这样似乎也无法解决问题,因为如果ASP.Net页面一次又一次快速提交,我也能够重现相同的错误。因此,我回到了寻找一个傻瓜证明解决方案在我的情况下

  string state = null;
  int i = 0;
  while (i < 20) {
    try {

        state = File.ReadAllText(Server.MapPath(string.Format("~/state/{0}", fileName)));

    } catch (Exception ex2) {
        //log exception
        Elmah.ErrorSignal.FromCurrentContext().Raise(ex2);
        //if even retry doesn't work then throw an exception
        if (i == 19) {
            throw;
        }
        //sleep for a few milliseconds
        System.Threading.Thread.Sleep(10);
    }
    i++;
  }

  i = 0;
  while (i < 20) {
    try {


        File.WriteAllText(Server.MapPath(string.Format("~/state/{0}", fileName)), newState);

    } catch (Exception ex2) {
        //log exception
        Elmah.ErrorSignal.FromCurrentContext().Raise(ex2);
        //if even retry doesn't work then throw an exception
        if (i == 19) {
            throw;
        }
        //sleep for a few milliseconds
        System.Threading.Thread.Sleep(10);
    }
    i++;
  }
上述方法唯一的缺点是,当最终用户发回相同的ASP.Net页面时,会创建许多文件。所以,最好有一个后台作业来删除过时的文件,这样文件的数量就可以最小化

带顺序的文件名

更新3

另一个简单的解决方案是在读和写文件名之间交替使用。这样,我们就不会创建很多文件,而只会在最终用户多次发回同一页面时使用2个文件。该代码与更新2下的代码相同,但
文件排序后的代码
注释应替换为下面的代码

if (fileName.LastIndexOf("-seq_1") >= 0) {
            fileName = fileName.Substring(0, fileName.LastIndexOf("-seq_1"));
        } else {
            fileName = fileName + "-seq_1";
        }
采用交替方法的文件名

我猜文件读取操作有时在写入操作发生之前没有关闭文件,或者可能是文件写入操作在web应用程序发出下一个请求之前没有关闭文件

对。文件系统不支持原子更新。(尤其不是在窗户上;有许多怪癖。)

使用
FileStream
没有帮助。您只需重写
文件
类拥有的相同代码<代码>文件内部没有魔法。为了方便起见,它只使用了
FileStream
wrapped

尝试保持文件不变。当你想写一个新的内容时,写一个新的文件。将序列号附加到文件名(例如
ToString(“D9”)
)。读取时,选择序列号最高的文件

或者,只需添加一个具有小延迟的重试循环

或者,使用更好的数据存储,如数据库。文件系统真的很糟糕。这是一个很容易用SQLServer解决的问题

我猜文件读取操作有时不是在写入操作发生之前关闭文件

尽管根据文档,关闭的时间不能保证在方法返回之前发生:关闭可以异步完成


解决此问题的一种方法是将结果写入同一目录中的临时文件,然后将新文件移到旧文件的位置。

这个问题有点误导。
文件
类型上的静态方法只是使用(FileStream)方便地包装
,因此它们是静态的完全无关。您的问题在于并发性,即两个进程试图同时对同一文件执行操作。如何规避这些问题在许多其他问题中都有处理,请尝试搜索。相关:,,…@CodeCaster,此问题发生在我的开发机器上。我是唯一在笔记本电脑上运行Visual Studio 2013 ASP.Net应用程序的人。那么为什么要并发呢?我会禁用机器上运行的任何反恶意软件,看看问题是否消失。我有一个轶事经验,一些AV会比文件的实际用户打开文件的时间更长。@MichaelBurr,这是一个很好的观点。我在某个地方读到,反病毒软件可能导致文件被打开的时间比预期的长。我认为这个并发问题需要一种防御性的编程方法,比如重试循环或文件排序,如果使用这两种方法,效果会更好。因此,目标是最小化并发错误,而不是100%克服它们。我将尝试使用线程进行重试循环。睡眠(10)和循环计数器我尝试使用“更新1”下的代码中的重试循环,但如果我多次快速提交ASP.Net页,仍然能够重现相同的错误。如果单击网格寻呼机中的页码,我的Asp.Net页面将被提交。因此,即使是重试循环也不是一个真正的傻瓜式解决方案。如果这个循环不起作用,那么文件就会永久打开。找到它是什么并杀死它。或者可能是增加睡眠间隔和最大循环数,在我的代码中最大循环数是20,睡眠间隔是10毫秒。更新2和更新3下的解决方案没有任何并发错误,它们看起来很简单。谢谢你在这方面的想法。这有记录吗?如果系统不允许同时打开一个文件(这在windows上很常见),那么这就意味着关闭可以异步进行,这是不合理的。按照这个逻辑,你可以对几乎任何有副作用但在返回值中无法直接观察到的方法说一些类似的话。@MichaelBurr为什么?除非一种方法因其副作用而被调用,并且当副作用在返回值中不可直接观察时,让该方法完全合理
if (fileName.LastIndexOf("-seq_1") >= 0) {
            fileName = fileName.Substring(0, fileName.LastIndexOf("-seq_1"));
        } else {
            fileName = fileName + "-seq_1";
        }