它是可以放心使用File类的静态方法在C#中? [英] Is it safe to use static methods on File class in C#?

查看:176
本文介绍了它是可以放心使用File类的静态方法在C#中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经按照一个ASP.Net应用程序,其中一个文件被读取后写入文件的代码隐藏代码。



代码

  VAR状态= File.ReadAllText(使用Server.Mappath(的String.Format(〜/国家/ {0 }, 文件名))); 
如果(state.indexOf(1)== 0)
{
File.WriteAllText(使用Server.Mappath(的String.Format(〜/国家/ {0},文件名)),newState);
}



有时,但并非总是如此,我得到下面的异常。



例外



进程无法访问该文件C:\inetpub\wwwroot\mywebsite1\state\20150905005929435_edf9267e-fad1-45a7-bfe2-0e6e643798b5'。因为它正被另一个进程使用



我猜测,该文件的读取操作,有时不关闭文件的写操作之前发生,或者可以是下一个请求之前,文件的写操作不被关闭文件从Web应用程序来。但是,我找不到究竟是什么原因



:我怎样才能避免这种错误的发生?这难道不是安全使用文件类,而是使用FileStream对象的传统方法,我将永远处置FileStream对象明确?



更新1



我试过重试循环的办法,但即使这样似乎没有解决的问题,因为我能如果ASP.Net网页被多次提交很快陆续重现同样的错误。所以我回发现我的情况一个很简单的解决方案。



 字符串状态= NULL; 
INT I = 0;
,而(I< 20){
尝试{

=状态File.ReadAllText(使用Server.Mappath(的String.Format(〜/国家/ {0},文件名)));

}赶上(例外EX2){
//记录异常
Elmah.ErrorSignal.FromCurrentContext()提高(EX2);
//如果连重试不起作用,那么抛出一个异常
如果(我== 19){
扔;
}
//睡了几毫秒
System.Threading.Thread.Sleep(10);
}
I ++;
}

I = 0;
,而(I< 20){
尝试{


File.WriteAllText(使用Server.Mappath(的String.Format(〜/国家/ {0} 文件名)),newState);

}赶上(例外EX2){
//记录异常
Elmah.ErrorSignal.FromCurrentContext()提高(EX2);
//如果连重试不起作用,那么抛出一个异常
如果(我== 19){
扔;
}
//睡了几毫秒
System.Threading.Thread.Sleep(10);
}
I ++;
}



更新2



这似乎工作的唯一万无一失的解决方案是通过使用的文件排序的办法,由 USR 建议。这涉及写入到不同的文件,而不是刚读同一个文件。文件的名字被写入就是刚才读取序列号附加文件的名称。

 字符串文件名= hiddenField1.Value; 
串状态= NULL;
INT I = 0;
,而(I< 20){
尝试{

=状态File.ReadAllText(使用Server.Mappath(的String.Format(〜/国家/ {0},文件名)));

}赶上(例外EX2){
//记录异常
Elmah.ErrorSignal.FromCurrentContext()提高(EX2);
//如果连重试不起作用,那么抛出一个异常
如果(我== 19){
扔;
}
//睡了几毫秒
System.Threading.Thread.Sleep(10);
}
I ++;
}

I = 0;
,而(I< 20){
尝试{
// *************** FILE测序********* *****************
//更改文件到状态写入,所以没有并发错误发生
//读取之间,距离和写入相同文件。这是一个很简单的解决方案。
//由于整的最大值超过2十亿即2,147,483,647
//所以我们可以肯定,我们的序列将永远不会用完,因为一个ASP.Net页面
//限制不会回传2十亿倍
如果(fileName.LastIndexOf( - seq_)> = 0){
文件名= fileName.Substring(0,fileName.LastIndexOf( - seq_) + 4 + 1)+(int.Parse(fileName.Substring(fileName.LastIndexOf( - seq_)+ 4 + 1))+ 1);
}其他{
文件名=文件名+-seq_1
}
//改变,因此在明年读操作的新文件读取
hiddenField1.Value =文件名的文件名;
File.WriteAllText(使用Server.Mappath(的String.Format(〜/国家/ {0},文件名)),newState);

}赶上(例外EX2){
//记录异常
Elmah.ErrorSignal.FromCurrentContext()提高(EX2);
//如果连重试不起作用,那么抛出一个异常
如果(我== 19){
扔;
}
//睡了几毫秒
System.Threading.Thread.Sleep(10);
}
I ++;
}



唯一的缺点上面的方法是很多的文件将作为最终用户获得创建后回到同一页面ASP.Net。因此,这将是很好的有一个删除过时的文件,这样的文件的数量将减少后台作业。



文件名以测序





更新3



另一种万无一失的解决方法是交替读取和写入的文件名。这样,我们最终不会创建多个文件,并只使用2个文件作为最终用户回发到同一页面多次。该代码文件测序评论应该由下面的代码替换后相同的update 2除了代码的代码。

 如果(fileName.LastIndexOf( -  seq_1)> = 0){
文件名= fileName.Substring(0,fileName.LastIndexOf( - seq_1)) ;
}其他{
文件名=文件名+-seq_1
}



文件名以交替的方式


解决方案

我猜测,文件读取操作有时未关闭文件的写操作之前发生,或者可以是从web应用的下一个请求到来之前的文件的写操作不被关闭文件




正确的。文件系统不支持原子更新好。 (特别不能在Windows;多怪癖)



使用的FileStream 于事无补。你只需重写了文件类有相同的代码。 文件有没有神奇的内部。它只是使用的FileStream 包裹为您提供方便。



尽量保持文件不变。当你想要写一个新的内容写入新文件。附加一个序列号的文件名(如的ToString(D9))。当读挑最高序列号的文件。



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



或者,使用更好的数据存储,例如数据库。文件系统是真是可恶。这是与SQL Server为例容易解决的问题。


I have following code in code-behind of an ASP.Net app, where a file is being read followed by writing to the file.

Code

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);
}

Sometimes, but not always, I get the following exception.

Exception

The process cannot access the file 'C:\inetpub\wwwroot\mywebsite1\state\20150905005929435_edf9267e-fad1-45a7-bfe2-0e6e643798b5' because it is being used by another process.

I am guessing that the file read operation sometimes is not closing the file before the write operation happens, Or may be the file write operation is not closing the file before the next request from web application comes. But, I cannot find what exactly is the reason.

Question: How can I avoid this error from happening? Is it not safe to use the File class and instead use the traditional approach of FileStream object where I would always dispose the FileStream object explicitly?

UPDATE 1

I tried a retry loop approach, but even that didn't seem to solve the problem , since I was able to reproduce the same error if the ASP.Net page was submitted very quickly multiple times one after another. So I am back to finding a fool-proof solution in my case.

  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++;
  }

UPDATE 2

The only fool proof solution that seemed to work is by using a File Sequencing approach, as suggested by usr. This involves writing to a different file and not to the same file that was just read. The name of file being written to is the name of file that was just read appended by a sequence number.

  string fileName = hiddenField1.Value;
  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 SEQUENCING**************************
        //Change the file to which state is written, so no concurrency errors happen 
        //between reading from and writing to same file. This is a fool-proof solution.
        //Since max value of integer is more than 2 billion i.e. 2,147,483,647
        //so we can be sure that our sequence will never run out of limits because an ASP.Net page
        //is not going to postback 2 billion times
        if (fileName.LastIndexOf("-seq_") >= 0) {
            fileName = fileName.Substring(0, fileName.LastIndexOf("-seq_") + 4 + 1) + (int.Parse(fileName.Substring(fileName.LastIndexOf("-seq_") + 4 + 1)) + 1);
        } else {
            fileName = fileName + "-seq_1";
        }
        //change the file name so in next read operation the new file is read
        hiddenField1.Value = fileName;
        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++;
  }

The only downside to above approach is that many files would get created as end users post back to the same ASP.Net page. So, it would be good to have a background job that deleted stale files so number of files would be minimized.

File Names with sequencing

UPDATE 3

Another fool proof solution is to alternate between read and write file names. This way we do not end up creating many files and only use 2 files as the end user posts back to the same page many times. The code is same as in code under UPDATE 2 except the code after FILE SEQUENCING comment should be replaced by code below.

if (fileName.LastIndexOf("-seq_1") >= 0) {
            fileName = fileName.Substring(0, fileName.LastIndexOf("-seq_1"));
        } else {
            fileName = fileName + "-seq_1";
        }

File Names with Alternating approach

解决方案

I am guessing that the file read operation sometimes is not closing the file before the write operation happens, Or may be the file write operation is not closing the file before the next request from web application comes.

Correct. File systems do not support atomic updates well. (Especially not on Windows; many quirks.)

Using FileStream does not help. You would just rewrite the same code that the File class has. File has no magic inside. It just uses FileStream wrapped for your convenience.

Try keeping files immutable. When you want to write a new contents write a new file. Append a sequence number to the file name (e.g. ToString("D9")). When reading pick the file with the highest sequence number.

Or, just add a retry loop with a small delay.

Or, use a better data store such as a database. File systems are really nasty. This is an easy problem to solve with SQL Server for example.

这篇关于它是可以放心使用File类的静态方法在C#中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆