0

I have an app that reads from text files to determine which reports should be generated. It works as it should most of the time, but once in awhile, the program deletes one of the text files it reads from/writes to. Then an exception is thrown ("Could not find file") and progress ceases.

Here is some pertinent code.

First, reading from the file:

List<String> delPerfRecords = ReadFileContents(DelPerfFile);

. . .

private static List<String> ReadFileContents(string fileName)
{
    List<String> fileContents = new List<string>();
    try
    {
        fileContents = File.ReadAllLines(fileName).ToList();
    }
    catch (Exception ex)
    {
        RoboReporterConstsAndUtils.HandleException(ex);
    }
    return fileContents;
}

Then, writing to the file -- it marks the record/line in that file as having been processed, so that the same report is not re-generated the next time the file is examined:

MarkAsProcessed(DelPerfFile, qrRecord);

. . .

private static void MarkAsProcessed(string fileToUpdate, string 
qrRecord)
{
    try
    {
        var fileContents = File.ReadAllLines(fileToUpdate).ToList();
        for (int i = 0; i < fileContents.Count; i++)
        {
            if (fileContents[i] == qrRecord)
            {
                fileContents[i] = string.Format("{0}{1} {2}"
qrRecord, RoboReporterConstsAndUtils.COMPLETED_FLAG, DateTime.Now);
            }
        }
        // Will this automatically overwrite the existing?
        File.Delete(fileToUpdate);
        File.WriteAllLines(fileToUpdate, fileContents);
    }
    catch (Exception ex)
    {
        RoboReporterConstsAndUtils.HandleException(ex);
    }
}

So I do delete the file, but immediately replace it:

File.Delete(fileToUpdate);
File.WriteAllLines(fileToUpdate, fileContents);

The files being read have contents such as this:

Opas,20170110,20161127,20161231-COMPLETED 1/10/2017 12:33:27 AM
Opas,20170209,20170101,20170128-COMPLETED 2/9/2017 11:26:04 AM
Opas,20170309,20170129,20170225-COMPLETED
Opas,20170409,20170226,20170401

If "-COMPLETED" appears at the end of the record/row/line, it is ignored - will not be processed.

Also, if the second element (at index 1) is a date in the future, it will not be processed (yet).

So, for these examples shown above, the first three have already been done, and will be subsequently ignored. The fourth one will not be acted on until on or after April 9th, 2017 (at which time the data within the data range of the last two dates will be retrieved).

Why is the file sometimes deleted? What can I do to prevent it from ever happening?

If helpful, in more context, the logic is like so:

internal static string GenerateAndSaveDelPerfReports()
{
    string allUnitsProcessed = String.Empty;
    bool success = false;
    try
    {
        List<String> delPerfRecords = ReadFileContents(DelPerfFile);
        List<QueuedReports> qrList = new List<QueuedReports>();
        foreach (string qrRecord in delPerfRecords)
        {
            var qr = ConvertCRVRecordToQueuedReport(qrRecord);
            // Rows that have already been processed return null
            if (null == qr) continue;
            // If the report has not yet been run, and it is due, add i
to the list
            if (qr.DateToGenerate <= DateTime.Today)
            {
                var unit = qr.Unit;
                qrList.Add(qr);
                MarkAsProcessed(DelPerfFile, qrRecord);
                if (String.IsNullOrWhiteSpace(allUnitsProcessed))
                {
                    allUnitsProcessed = unit;
                }
                else if (!allUnitsProcessed.Contains(unit))
                {
                    allUnitsProcessed = allUnitsProcessed + " and "  
unit;
                }
            }
        }
        foreach (QueuedReports qrs in qrList)
        {
            GenerateAndSaveDelPerfReport(qrs);
            success = true;
        }
    }
    catch
    {
        success = false;
    }
    if (success)
    {
        return String.Format("Delivery Performance report[s] generate
for {0} by RoboReporter2017", allUnitsProcessed);
    }
    return String.Empty;
}

How can I ironclad this code to prevent the files from being periodically trashed?

UPDATE

I can't really test this, because the problem occurs so infrequently, but I wonder if adding a "pause" between the File.Delete() and the File.WriteAllLines() would solve the problem?

UPDATE 2

I'm not absolutely sure what the answer to my question is, so I won't add this as an answer, but my guess is that the File.Delete() and File.WriteAllLines() were occurring too close together and so the delete was sometimes occurring on both the old and the new copy of the file.

If so, a pause between the two calls may have solved the problem 99.42% of the time, but from what I found here, it seems the File.Delete() is redundant/superfluous anyway, and so I tested with the File.Delete() commented out, and it worked fine; so, I'm just doing without that occasionally problematic call now. I expect that to solve the issue.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
B. Clay Shannon-B. Crow Raven
  • 8,547
  • 144
  • 472
  • 862
  • 3
    Honest question: Don't you ever get bored of asking questions instead of going off and trying to figure this out on your own? You are now the top question asker of all time on Stack Overflow and I can't quite get my head round it! – DavidG Mar 15 '17 at 21:56
  • 1
    A `Thread.Sleep(int.MaxValue);` will make it occur even more infrequent. Put it before `File.Delete();` and the file will not disappear for the next 49 days. – Thomas Weller Mar 15 '17 at 22:18
  • 1
    @ThomasWeller Now *that's* a proper troll answer! – DavidG Mar 15 '17 at 22:23
  • @Danny_ds: you're right; but it takes an int only, so it's some 24 days or so – Thomas Weller Mar 15 '17 at 22:46
  • 1
    interestingly, there is [this](http://stackoverflow.com/a/33508079/1132334) and [this](https://social.msdn.microsoft.com/Forums/vstudio/en-US/63f7d11b-5b1e-4e9f-a468-1ff271a0b341/how-to-delete-a-file-immediately-in-c?forum=netfxbcl), supporting the theory that under certain circumstances, on certain file systems, deletion is delayed. – Cee McSharpface Mar 15 '17 at 23:04
  • @dlatikay: Thanks for that; the second link has this:"just use something like File.WriteAllText where if the file exists, the data is just overwritten, if the file does not exist it will be created." which makes me feel more secure in 86ing the File.Delete(). It was, indeed, apparently a spinning of wheels only. – B. Clay Shannon-B. Crow Raven Mar 15 '17 at 23:18
  • Well, actually that's exactly what I meant in my (troll) answer :) - so I undeleted it now with some update. – Danny_ds Mar 15 '17 at 23:45

1 Answers1

1
// Will this automatically overwrite the existing?
File.Delete(fileToUpdate);
File.WriteAllLines(fileToUpdate, fileContents);

I would simply add an extra parameter to WriteAllLines() (which could default to false) to tell the function to open the file in overwrite mode, and not call File.Delete() at all then.

Do you currently check the return value of the file open?


Update: ok, it looks like WriteAllLines() is a .Net Framework function and therefore cannot be changed, so I deleted this answer. However now this shows up in the comments, as a proposed solution on another forum:

"just use something like File.WriteAllText where if the file exists, the data is just overwritten, if the file does not exist it will be created."

And this was exactly what I meant (while thinking WriteAllLines() was a user defined function), because I've had similar problems in the past.

So, a solution like that could solve some tricky problems (instead of deleting/fast reopening, just overwriting the file) - also less work for the OS, and possibly less file/disk fragmentation.

Danny_ds
  • 11,201
  • 1
  • 24
  • 46
  • @ThomasWeller I just don't think that Danny know that `File.WriteAllLines` is a .Net Framework function and cannot be changed... – DavidG Mar 15 '17 at 22:14
  • @ThomasWeller What do you mean by that? I've had similar problems in the past with quickly closing/opening files. In this case not deleting the file at all and instead overwriting it would at least eliminate that possible problem? Or am I overlooking something? – Danny_ds Mar 15 '17 at 22:15
  • @DavidG Thanks for that :) Indeed I thought it was a user function (I'm not a C# programmer) - If that's the case I'll remove this answer. – Danny_ds Mar 15 '17 at 22:16
  • 1
    Oh, getting upvotes now.. :) - I'll remove it in a moment – Danny_ds Mar 15 '17 at 22:19