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.
// 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.
I want ask an easy question about my code in c# .... I know that there are lot of topics with same or similar topic/code result. But I need to hand in my code to school, so I can't just use the best solution on Stackoverflow or another page. I showed my code to my teacher and now need to fix a little bug.
The Code is about backing up files with a console report, so in first step I check if a folder exists. Second step is to report that the folder exists or doesn't exist, if it doesn't the code creates this folder and rechecks ...
SITUATION : CONSOLE REPORT
folders doesnt exist:
02:02:06 directory for backup Exist ... can continue
02:02:05 directory for backup DOESNT EXIST ... creating required folders...
folders exist :
02:02:55 directory for backup Exist ... can continue
02:02:54 directory for backup Exist ... can continue
In the 1st example the report is OK, but in the 2nd, my code tells me the same information twice... i just can't get my code to work properly..
Here is my code:
public void checkbackupfolders() {
do {
create_backup_folders();
} while (create_backup_folders() == false);
}
public bool create_backup_folders()
{
string path = "\\BACKUP\\" + Globals.hostname;
if (Directory.Exists(path))
{
consolecho("directory for backup Exist ... can continue");
return true;
}
else
{
consolecho("directory for backup DOESNT EXIST ... creating required folders...");
Directory.CreateDirectory("\\BACKUP\\" + Globals.hostname);
return false;
}
}
Why are you calling the method twice here?:
do {
create_backup_folders();
} while (create_backup_folders() == false);
That's going to make things confusing, as you're now discovering. Just call the method once on each loop iteration and store the result of the method. Then use that stored result in the loop condition:
var canContinue = false;
do {
canContinue = create_backup_folders();
} while (canContinue == false);
I have no doubt that this questions are of very basic character but for me it's quite hard...
I want to select a filename/path with saveFileDialog.
Like this:
private void saveFileDialog1_FileOk(object sender, CancelEventArgs e)
{
// Get file name.
string name = saveFileDialog1.FileName;
Source: http://www.dotnetperls.com/savefiledialog
Then I want to declare StreamWriter with the Path from above (suppose this works like this)
System.IO.StreamWriter file = new System.IO.StreamWriter(saveFileDialog1_FileOk.FileName);
This had to be done in the saveFileDialog1_FileOk function. Then I want to write to that file from another function/event handler (SerialPort has received data). And the filename should be able to change to a new file while runtime so that the data from the eventhandler
is written to another file.
But from that other function I'm not able to access the StreamWriter and I don't know how to change the file.
Also I would like to know how I can make the Data in the file accessable during runtime. In my first tests it's always written to the file when I call
file.Close()
b
ut then I cannot open it again.
It would be a great help if anyone could suggest me the way to go...
If I understood your question correctly; the approach you have selected seems to be violating the principle of single responsibility apart from causing you all those issues.
May be what you should consider doing is to create a class that manages the a static instance of stream and use it from open dialog and serial port events
Lets call the stream management class StreamManager. It would have an implementation similer to the following code
public class StreamManager
{
private static StreamWriter file = null;
public void NewFile(string filePath)
{
this.close();
file = new StreamWriter(filePath);
}
public void WriteToFile(string yourData)
{
file.Write(yourData);
}
public void close()
{
if (file != null /* and if not already been closed*/)
{
file.Flush();
file.Close();
}
}
}
Now you may call from new StreamManager().NewFile(fileName) open dialog and new StreamManager ().WriteToFile(…) from serial port.
Please note I have not tested the above code and provided only as a guide. You should add more state / error management to all methods
Cheers
I've already wrote my code with my try catch and extra message box but now i have to put the message box into a resource file how can i do it?
This is my code:
public void btnUpload_Click(object sender, EventArgs e)
{
try
{
// in the filepath variable we are going to put the path file that we browsed.
filepath = txtPath.Text;
if (filepath == string.Empty)
{
MessageBox.Show("No file selected. Click browse and select your designated file.");
}
}
You can just add those messages as String in your main application Resource file using the designer (Resources.resx) and then access them using Properties namespace. Let's say you add this:
ErrorNoFile | "No file selected. Click browse and select your designated file."
You can just call it like so:
MessageBox.Show(Properties.Resources.ErrorNoFile);
And if you modify the entry name in the resource file, it will be automatically refactored, at least with VS2012 which is the one I'm using. Instanciating a ResourceManager is only good if you want to keep those messages in a separate resource, otherwise it looks like an overkill to me.
// Create a resource manager to retrieve resources.
ResourceManager rm = new ResourceManager("items", Assembly.GetExecutingAssembly());
// Retrieve the value of the string resource named "filepath".
// The resource manager will retrieve the value of the
// localized resource using the caller's current culture setting.
public void btnUpload_Click(object sender, EventArgs e)
{
try
{
// in the filepath variable we are going to put the path file that we browsed.
filepath = txtPath.Text;
if (filepath == string.Empty)
{
String str = rm.GetString("welcome");
MessageBox.Show(str);
}
}
I am trying to create a file with a FileInfo object and I am getting strange behavior.
Here is the gist of what I am doing -
public void CreateLog()
{
FileInfo LogFile = new FileInfo("");
if (!LogFile.Directory.Exists) { LogFile.Directory.Create(); }
if (!LogFile.Exists) { LogFile.Create(); }
if (LogFile.Length == 0)
{
using (StreamWriter Writer = LogFile.AppendText())
{
Writer.WriteLine("Quotes for " + Instrument.InstrumentID);
Writer.WriteLine("Time,Bid Size,Bid Price,Ask Price,Ask Size");
}
}
}
However, when it checks to see the length of the logfile, it says that the file does not exist (I checked - it does exist).
When I substitute LogFile.Length with the following:
File.ReadAllLines(LogFile.FullName).Length;
Then I get an exception that says that it cannot access the file because something else is already accessing it.
BUT, if I do a Thread.Sleep(500) before I do ReadAllLines, then it seems to work fine.
What am I missing?
LogFile.Create() if you user this function ,you may lock the file, so you can use using ,like this
using(LogFile.Create()){}
after that you can use the file again