Search code examples
c#filedictionaryio

Why doesn't File.WriteAllText write data to file?


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.


Solution

  • 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.