Click here to Skip to main content
15,881,898 members
Articles / Programming Languages / C#
Tip/Trick

Where Should I Store My Data?

Rate me:
Please Sign up or sign in to vote.
5.00/5 (34 votes)
20 Apr 2012CPOL2 min read 130.8K   44   21
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)


Written By
CEO
Wales Wales
Born at an early age, he grew older. At the same time, his hair grew longer, and was tied up behind his head.
Has problems spelling the word "the".
Invented the portable cat-flap.
Currently, has not died yet. Or has he?

Comments and Discussions

 
QuestionCalculator hide application Pin
Rajput Poonam20-Apr-21 8:21
Rajput Poonam20-Apr-21 8:21 
BugAppGuid property limitation... Pin
cchester13-Apr-20 6:23
cchester13-Apr-20 6:23 
Questionmy vote of 5 Pin
Michael Haephrati22-Oct-17 2:13
professionalMichael Haephrati22-Oct-17 2:13 
QuestionMy vote of 5 Pin
Bob Stoom24-Jan-16 1:07
professionalBob Stoom24-Jan-16 1:07 
QuestionSuperb! Pin
Mark@Metro23-Mar-15 17:56
Mark@Metro23-Mar-15 17:56 
Question[My vote of 2] Static gettters Pin
Истинското Ми-Име26-Jun-14 12:46
Истинското Ми-Име26-Jun-14 12:46 
AnswerRe: [My vote of 2] Static gettters PinPopular
PIEBALDconsult26-Jun-14 12:55
mvePIEBALDconsult26-Jun-14 12:55 
SuggestionChecking Directory exists Pin
PabloInNz10-Jun-14 18:07
PabloInNz10-Jun-14 18:07 
GeneralNice work ! Pin
PabloInNz10-Jun-14 16:27
PabloInNz10-Jun-14 16:27 
GeneralMy vote of 5 Pin
Maciej Los3-May-14 3:18
mveMaciej Los3-May-14 3:18 
Questionabout the guid Pin
User349011-Jun-13 8:25
User349011-Jun-13 8:25 
AnswerRe: about the guid Pin
OriginalGriff11-Jun-13 8:34
mveOriginalGriff11-Jun-13 8:34 
GeneralRe: about the guid Pin
User349011-Jun-13 8:42
User349011-Jun-13 8:42 
GeneralMy vote of 5 Pin
Menon Santosh11-Mar-13 2:05
professionalMenon Santosh11-Mar-13 2:05 
QuestionSaving in registry Pin
Thomas Daniels19-Jan-13 4:54
mentorThomas Daniels19-Jan-13 4:54 
AnswerRe: Saving in registry Pin
PIEBALDconsult19-Jan-13 5:11
mvePIEBALDconsult19-Jan-13 5:11 
QuestionWhat about parent of LocalUserAppDataPath Pin
LesF18-Aug-12 16:01
LesF18-Aug-12 16:01 
AnswerRe: What about parent of LocalUserAppDataPath Pin
OriginalGriff18-Aug-12 20:12
mveOriginalGriff18-Aug-12 20:12 
QuestionOk..but Pin
FatCatProgrammer21-Apr-12 4:16
FatCatProgrammer21-Apr-12 4:16 
GeneralGood Tip Pin
perilbrain20-Apr-12 10:08
perilbrain20-Apr-12 10:08 
GeneralRe: Good Tip Pin
OriginalGriff20-Apr-12 21:43
mveOriginalGriff20-Apr-12 21:43 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.