4

I've got a problem that has apparently been encountered before, though the solutions then don't help with what I'm seeing.

I'm trying to write data to a file, based on a base 64 encoding of the contents of a Dictionary<string, object>. It's supposed to go like this: if the file doesn't exist,

  1. It gets created
  2. Some default values get added to the Dictionary
  3. The Dictionary is turned into base 64 data
  4. The data is written to the file

Steps 1 through 3 are working fine. Step 4 appears to be working fine, until you I open the file - when it's revealed that there's nothing in it. It gets created but not written to, and I end up with an empty file.

The code goes like this, where the top method (CreateDefaultConfigFile is called if the file doesn't exist):

private static void CreateDefaultConfigFile()
{
    Console.WriteLine("AppConfig.CreateDefaultConfigFile");
    File.Create(CONFIG_FILE_PATH);
    Configuration.Clear();
    Configuration.Add("ClientId", "5577");
    Configuration.Add("RedirectUri", "https://stackexchange.com/oauth/login_success");
    Configuration.Add("RequestKey", "2WQ5ksCzcYLeeYJ0qM4kHw((");
    Save();
}

public static void Save()
{
    Console.WriteLine("AppConfig.Save");
    string data = "";
    foreach (KeyValuePair<string, object> pair in Configuration)
    {
        Console.WriteLine("  {0}: {1}", pair.Key, pair.Value.ToString());
        if (pair.Value.GetType() == typeof(string))
        {
            data += pair.Key + SC_SPLITTER + pair.Value + "\n";
        }
        else if (pair.Value.GetType() == typeof(Array))
        {
            data += pair.Key + SC_SPLITTER + "[" + string.Join(",", pair.Value) + "]\n";
        }
        else
        {
            Configuration.Remove(pair.Key);
        }
    }
    if (data.EndsWith("\n"))
    {
        data.Remove(data.Length - 2);
    }
    byte[] dataBytes = Encoding.UTF8.GetBytes(data);
    string encoded = Convert.ToBase64String(dataBytes);
    File.WriteAllText(CONFIG_FILE_PATH, encoded);
    Console.WriteLine("  Written to file.");
}

Important fact to note: the "Written to file." message never gets logged to the console (though if I put a log directly before the File.WriteAllLines call, it does log). A breakpoint at the final Console.Log call never raises.

No exceptions are thrown, and it's not because data or encoded are empty - logging them just before the write reveals data in both.

CONFIG_FILE_PATH is a constant value of C:\Users\Me\Documents\longpath\bin\Debug\config\general.cgf.

I've also tried using a FileStream and FileStream.Flush(), as suggested in the question I linked at the top - this doesn't work.

ArtOfCode
  • 5,702
  • 5
  • 37
  • 56
  • 1
    You can't remove items during a for-each. – LarsTech Sep 16 '15 at 18:11
  • @LarsTech No? Ah well, no matter. That situation hasn't been encountered yet. – ArtOfCode Sep 16 '15 at 18:12
  • Maybe `data` is empty? – Kenney Sep 16 '15 at 18:14
  • @Kenney A log of that, and of `encoded` just before writing the file, reveals data in both of them. – ArtOfCode Sep 16 '15 at 18:14
  • 2
    use the debugger and step through the code.. also if worse comes to worse and you want to catch exceptions wrap your code around a try{}catch{} and handle the IO.Exception in the catch{} – MethodMan Sep 16 '15 at 18:15
  • What is `CONFIG_FILE_PATH`? (which should probably be renamed to follow C# conventions, btw) – Dave Zych Sep 16 '15 at 18:15
  • @DaveZych RTQ. CONFIG_FILE_PATH is explained in the question. – ArtOfCode Sep 16 '15 at 18:15
  • also show how the `CONFIG_FILE_PATH` is declared and or defined have you considered using a StreamWriter as well as calling Flush() after each write also where are you closing the file you are writing to..? – MethodMan Sep 16 '15 at 18:17
  • 1
    @MethodMan I believe `File.WriteAllText` self-closes, because there's no instance to close. `CONFIG_FILE_PATH` is simply defined as a `private static readonly string`, only ever assigned once. – ArtOfCode Sep 16 '15 at 18:18
  • 1
    (btw `File.Create` is not necessary); What happens if you do a `File.WriteAllText("some_other_file", "testcontent")` at the position of the last log line that has output? And, it can't be that `.cgf` typo, can it? – Kenney Sep 16 '15 at 18:19
  • what I am getting at is if you post code you need to post all relevant code so that others can see how you have declared or defined it.. for all we know `CONFIG_FILE_PATH` can be a constant string without a path.. – MethodMan Sep 16 '15 at 18:20
  • 1
    I guess to keep piling on, you should be using a StringBuilder for that data variable, and `pair.Value.ToString()`. – LarsTech Sep 16 '15 at 18:20
  • @Kenney Removing the `File.Create` call seems to have solved the problem. I guess because the Create process hogs the file for too long? Thanks, anyway. – ArtOfCode Sep 16 '15 at 18:24
  • @ArtOfCode Since you create a file with `File.Create(CONFIG_FILE_PATH);` `File.WriteAllText` can not not write to it before it is closed. Just remove `File.Create` – Eser Sep 16 '15 at 18:24
  • _"No exceptions are thrown"_ - I don't believe that. `File.Create()` leaves the file open, so `File.WriteAllText()` _will_ throw. Perhaps the `try-catch` is at a call site lower on the stack, namely the code that calls `CreateDefaultConfigFile()`. – CodeCaster Sep 16 '15 at 18:27
  • @AustinWBryan It's not a good idea to edit code in questions. Code in questions is indicative of the problem being asked about; changing it means you may inadvertently remove the problem. The _only_ thing you should change is indentation to enhance readability; you shouldn't change any other formatting and you _absolutely_ shouldn't change syntax. – ArtOfCode Apr 24 '20 at 15:57

1 Answers1

5

The File.Create method doesn't do what you appear to think that it does.

It does create a file, but it also leaves the file open and returns a FileStream object to handle the open file.

If you just call Create and ignore the returned FileStream object, then the file will be left open until the object is disposed by the garbage collector.

Depending on when the garbage collection runs, the File.WriteAllText call will either be able to write to the file, or you will get an IOException. The fact that you don't see anything written to the file, and that you don't see the text that is written out after the call, suggests that you actually get an exception but catch that at some other level and ignore it.

If you want to use File.Create to create the file, you should get the FileStream object and dispose it to close the file:

using (FileStream stream = File.Create(CONFIG_FILE_PATH)) {
}

However, you don't have to create the file before calling File.WriteAllText, it will create the file if it doesn't exist.

Guffa
  • 687,336
  • 108
  • 737
  • 1,005