1

I thought I solved this problem by closing and disposing my reader, but still in some cases the file was in use. Next I called the Garbage Collector, so the file will be released. This solves 99% of all the issues which will bring up this error. Code used:

        public override void Finish()
        {
            // Kill the reader!
            if (_reader != null)
            {
                _reader.Close();
                _reader.Dispose();

                // Make sure the server doesn't hold the file
                GC.Collect();
            }
            DeleteFile();
        }

Finish is called after a big process which will handle the file's content.

When I process a file with only 1 (or very few) line I sometimes get this error. It seems windows is to quick and DeleteFile(); fails. It is very hard for me to reproduce this error, but sometimes it just happens twice in a row.
This never occurs when I process a file with takes more then 2 seconds to process.
I cannot use a using because files can be GB's, and windows doesn't like it when its memory is getting too full. Also, this way the process performs way better.

Question:
Is there anything else I can do to prevent this error?

PS: Feel free to ask for more information.

EDIT:
Code to delete a file

        protected void DeleteFile()
        {
            // Delete the file
            if (FileName != null && File.Exists(FileName))
                File.Delete(FileName);
        }

Code to create the file

        protected void WriteFile()
        {
            // Prepare variables
            string path = Path.GetTempPath();

            path += "\\MyTempFile";

            // Modifiy path to avoid overwriting an existing file.

            path += ".csv";

            // Write the file to the temp folder
            using (FileStream fs = new FileStream(path, FileMode.Create))
            {
                fs.Write(MyFile, 0, MyFile.Length);
            }

            // Was the writing successfully completed?
            _FileName = File.Exists(path) ? path : null;
        }

Code to create the reader

        protected override void ReadFile()
        {
            if (FileName == null)
                WriteFile();

            // Read the lines
            _reader = new StreamReader(FileName, Encoding.Default, true);
            while (_reader.Peek() != -1)
            {
                TotalRows++;
                _reader.ReadLine();
            }

            // Read the lines
            _reader = new StreamReader(FileName, Encoding.Default, true);
        }

I use an abstract class to determine how the input should be read. With the following statement I'll loop through the content of the file.

 while (FileReader.NextRow(out currentRow, out currentRowNumber))
        {
               // Process current Row...
        }

The method NextRow() Looks like this

        public override bool NextRow(out List<object> nextRow, out int rowNumber)
        {
            if (RowNumber > TotalRows)
            {
                nextRow = null;
                rowNumber = 0;

                return false;
            }

            // Set the row number to return
            rowNumber = RowNumber;

            // Prepare the row
            nextRow = _reader.ReadLine().ExtensionThatProcessesTheRow();
            RowNumber++;

            return true;
        }

After the while loop closes I call the finish process. FileReader.Finish();

Mixxiphoid
  • 1,044
  • 6
  • 26
  • 46
  • 5
    You do NOT need to call GC.Collect. You shouldn't, and it should have no impact on the ability to delete your file. – Joe May 31 '12 at 14:01
  • 2
    What does the code that accesses a file look like? We could make better suggestions there if we could see it. – Josh May 31 '12 at 14:02
  • 2
    We'd need to see all of the code that you are using to create, interact with, *and* delete these files. Chances are you are leaking an object somewhere, or that two threads are simultaneously interacting with the same file. – Chris Shain May 31 '12 at 14:03
  • @ChrisShain If I were leaking an object, I think the error should be consequent. Isn't it possible that a virusscanner/indexer/whatever is holding the file locked for deletion? – Mixxiphoid May 31 '12 at 15:34
  • @Joe Would you please explain why I shouldn't call GC.Collect? – Mixxiphoid May 31 '12 at 15:36
  • @Mixxiephoid no, virus scanners hook into the OS using special APIs for that purpose and generally do not block file deletion. They do not use normal I/O routines. – Chris Shain May 31 '12 at 16:23
  • @Mixxiphoid: let the garbage collector manage itself. Your call to .Dispose should be enough to release anything of importance. The lack of GC should not hold a file open. Calling GC.Collect in nearly all cases will actually make garbage collection worse. – Joe May 31 '12 at 17:14
  • @Joe thanks for the elaboration, I will remove that line of code. – Mixxiphoid May 31 '12 at 17:32

3 Answers3

1

As you are saying it only happens sometimes it may just be that the lock is held onto and that if you do a check then hopefully if it fails by the time you get round to doing another check on it then it should have had the time to get rid of the cached lock. If you use a method like this to check if the file is still being used:

public bool CheckIfFileIsBeingUsed(string fileName){

     try{    
         File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.None);
          }

      catch (Exception exp){
               return true;
        }

      return false;

}

And if it returns false then go ahead and delete the file, other wise wait and then run the check again.

Neil_M
  • 462
  • 5
  • 17
  • That could mean that I get stuck in a loop and isn't safe. While(FileIsInUse) { Thread.Sleep(100)} <-- Could make the computer wait for a very long long time. I did consider this ;). – Mixxiphoid May 31 '12 at 15:37
  • You could always make it into a do while loop and add a counter that breaks the loop after so many attempts and posts out a user friendly error saying the file was locked. – Neil_M May 31 '12 at 16:03
  • hmmm. I will give it a try, thanks for the tip. I will come back to this. – Mixxiphoid May 31 '12 at 17:33
0

Note: This should probably be a comment, but I need the additional space.

This code doesn't make sense:

// Prepare variables
string path = Path.GetTempPath();

// Write the file to the temp folder
using (FileStream fs = new FileStream(path, FileMode.Create))

Path is the directory for temporary files. You shouldn't be able to create a file with that name.

Additionally, here you use a variable called FileName:

protected void DeleteFile()
{
    // Delete the file
    if (FileName != null && File.Exists(FileName))
        File.Delete(FileName);
}

but in WriteFile you are using a variable called _FileName:

// Was the writing successfully completed?
_FileName = File.Exists(path) ? path : null;

My guess, based on the above, is that you are not writing what you think you are writing, or not deleting what you think that you are deleting.

Chris Shain
  • 50,833
  • 6
  • 93
  • 125
  • Believe me this works :P... As I said I work with abstract classes. the _FileName is the private property where FileName is the public property. – Mixxiphoid May 31 '12 at 17:27
  • What is the value of the variable `path` after calling `string path = Path.GetTempPath();` above? The documentation specifically states that this "Returns the path of the current user's temporary folder." http://msdn.microsoft.com/en-us/library/system.io.path.gettemppath.aspx – Chris Shain May 31 '12 at 17:34
  • I edited my question. I saw I forgot to copy some lines of code. I make it a .csv and add a (n) to make sure the filename is unique. – Mixxiphoid May 31 '12 at 17:36
0

I think I found the problem...
this reader wasn't disposed before reseting it in ReadFile().

        _reader = new StreamReader(FileName, Encoding.Default, true);
        while (_reader.Peek() != -1)
        {
            TotalRows++;
            _reader.ReadLine();
        }

I changed it to.

            using (var reader = new StreamReader(FileName, Encoding.Default, detectEncodingFromByteOrderMarks: true))
            {
                while (reader.Peek() != -1)
                {
                    TotalRows++;
                    reader.ReadLine();
                }
            }
Mixxiphoid
  • 1,044
  • 6
  • 26
  • 46