Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Where Should I Store My Data?

5.00/5 (35 votes)
20 Apr 2012CPOL2 min read 135.6K  
There is a common theme in development: storing data files in the application folder. This is bad, and wrong, and should be stamped on!
In this tip, you will see how to store data files and avoid problems arising from choosing the wrong location. You will also see how to get the path.

Introduction

Recently, I have been answering questions regarding storing data files, and the problems that result from choosing the wrong location. So, where should you store data? And how do you get the path?

Background

Windows has a number of places that are free for data storage and which will not cause "Access Denied" exceptions when you try to write to them - unfortunately, the default directory is not one of them. If you use code such as:

C#
File.WriteAllText("myFile.txt", "Hello World"); 

Then the system will try to write it to the current folder - which is normally the application executable directory, which in release programs is (or should be) under the "Program Files" or "Program Files(x86)" folder. It is not a good idea to write anything here - you may get an exception, or the user may not be able to locate it with Windows Explorer, or worse: it may well not be on a regular backup schedule folder list.

If you want to write data to a sensible location, Windows provides three Data folders:

User Data
Roaming User Data
All User Data

And these are accessible in C# via the Environment.SpecialFolder enumeration:

C#
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)

However, it is not a good idea to use these directly, as they are not specific to your application, but general to all.

One way to get an appropriate folder is just to use the Application Properties:

Application.UserAppDataPath
Application.LocalUserAppDataPath
Application.CommonAppDataPath

But I am not in favour of this as the path is specific to your application, and the version number of the assembly: If you issue a new version of your EXE, then it may not be able to find the data that worked with a previous version.

Instead, I prefer to use the Application GUID and the Environment.SpecialFolder enumeration.

When you create an assembly with Visual Studio, it is assigned a Guid value, which does not change during the life of that assembly - be it an EXE file or a Class Library. You can use that Guid to absolutely identify your application, and thus it's data - there is a very, very small chance of any two applications being given the same GUID - which is not the case for application names! How many companies do you think have wanted to call an application "Word", or "Paint"?

The table at the bottom shows the problem with using the Application Properties too blindly.

The Code

Include the following in your application data storage class, or in an appropriate Class Library.

C#
/// <summary>
/// Get the Application Guid
/// </summary>
public static Guid AppGuid
    {
    get
        {
        Assembly asm = Assembly.GetEntryAssembly();
        object[] attr = (asm.GetCustomAttributes(typeof(GuidAttribute), true));
        return new Guid((attr[0] as GuidAttribute).Value);
        }
    }
/// <summary>
/// Get the current assembly Guid.
/// <remarks>
/// Note that the Assembly Guid is not necessarily the same as the
/// Application Guid - if this code is in a DLL, the Assembly Guid
/// will be the Guid for the DLL, not the active EXE file.
/// </remarks>
/// </summary>
public static Guid AssemblyGuid
    {
    get
        {
        Assembly asm = Assembly.GetExecutingAssembly();
        object[] attr = (asm.GetCustomAttributes(typeof(GuidAttribute), true));
        return new Guid((attr[0] as GuidAttribute).Value);
        }
    }
/// <summary>
/// Get the current user data folder
/// </summary>
public static string UserDataFolder
    {
    get
        {
        Guid appGuid = AppGuid;
        string folderBase = Environment.GetFolderPath
                            (Environment.SpecialFolder.LocalApplicationData);
        string dir = string.Format(@"{0}\{1}\", folderBase, appGuid.ToString("B").ToUpper());
        return CheckDir(dir);
        }
    }
/// <summary>
/// Get the current user roaming data folder
/// </summary>
public static string UserRoamingDataFolder
    {
    get
        {
        Guid appGuid = AppGuid;
        string folderBase = Environment.GetFolderPath
                            (Environment.SpecialFolder.ApplicationData);
        string dir = string.Format(@"{0}\{1}\", 
                     folderBase, appGuid.ToString("B").ToUpper());
        return CheckDir(dir);
        }
    }
/// <summary>
/// Get all users data folder
/// </summary>
public static string AllUsersDataFolder
    {
    get
        {
        Guid appGuid = AppGuid;
        string folderBase = Environment.GetFolderPath
                            (Environment.SpecialFolder.CommonApplicationData);
        string dir = string.Format(@"{0}\{1}\", 
                     folderBase, appGuid.ToString("B").ToUpper());
        return CheckDir(dir);
        }
    }
/// <summary>
/// Check the specified folder, and create if it doesn't exist.
/// </summary>
/// <param name="dir"></param>
/// <returns></returns>
private static string CheckDir(string dir)
    {
    if (!Directory.Exists(dir))
        {
        Directory.CreateDirectory(dir);
        }
    return dir;
    }

Do note that if you include this code in a Class Library, then AppGuid will not be the same as the AssemblyGuid - a Class Library is a separate Assembly, and has its own Guid. This can be useful, if you want to store information which is to be used by multiple applications - User Login details for example if these are not related to the Windows Login details.

Sample Paths Generated

Common.AppGuid                     158aa00d-332e-440b-9c2c-47e2fc11c078
Common.AssemblyGuid                8d14ff7e-8d39-4163-a1ba-4fae4a5c361d
Common.UserDataFolder              C:\Users\griff\AppData\Local\
                                   {158AA00D-332E-440B-9C2C-47E2FC11C078}\
Application.UserAppDataPath        C:\Users\griff\AppData\Roaming\SetShares\SetShares\1.0.0.0
Common.UserRoamingDataFolder       C:\Users\griff\AppData\Roaming\
                                   {158AA00D-332E-440B-9C2C-47E2FC11C078}\
Application.LocalUserAppDataPath   C:\Users\griff\AppData\Local\SetShares\SetShares\1.0.0.0
Common.AllUsersDataFolder          C:\ProgramData\{158AA00D-332E-440B-9C2C-47E2FC11C078}\
Application.CommonAppDataPath      C:\ProgramData\SetShares\SetShares\1.0.0.0

History

  • 20th April, 2012: Original version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)