I'm predominantly a C# .NET 4.0 developer, and right now I'm reconsidering past approaches to storing and retrieving application settings.
In the past I've used a variety of methods, from simple 'flat' text files with tab separated key/values, to layered XML files, and a few other things in between. I've used in the past ( and am considering using again ) the built in App.Config/AppSettings classes, but there isn't any built in way to save data that you've loaded in from this source ( I could go into the difficulties I'm having in doing this but that's not the point of the question ).
The point here, is that all of these methods have their shortcomings, and while some work better than others, some are simpler to employ than others, and I am trying to nail down something that will be consistent and reliable.
My question, which will likely have a few valid answers so I'll be forced to pick the best one, is this: What if any built-in framework would 'you' recommend for both saving and loading data in and out of a settings file that can accompany the executable, or if not a built in framework, what standard would you recommend? XML? 'Flat' files? Stand-Alone Database?
Assume that the configuration itself won't be that large, and any real quantity of data will be read from a database ( or using some other technology ).
While Im a registry fan, and would rather have my settings there, with my c# apps I've used the settings that come built in - so under project properties you can define a load, and their default values, and then access them with
Properties.Settings.Default.<settingname>
and can set them too, so Properties.Settings.Default.HideDisabled = true;
and you can save them with
Properties.Settings.Default.Save();
Ive not had issues with this, but then, as you said, everything has a pro and a con.
Just yet another way (kernel32), it's not much about C# even C# 4.0 but as we have started list of variants it must be valid here
namespace Ini {
public class IniFile {
private string path;
[DllImport("kernel32")]
private static extern long WritePrivateProfileString(string section,
string key, string val, string filePath);
[DllImport("kernel32")]
private static extern int GetPrivateProfileString(string section,
string key, string def, StringBuilder retVal,
int size, string filePath);
public IniFile(string INIPath) {
path = INIPath;
}
public void IniWriteValue(string Section, string Key, string Value) {
WritePrivateProfileString(Section, Key, Value, this.path);
}
public string IniReadValue(string Section, string Key) {
StringBuilder temp = new StringBuilder(255);
int i = GetPrivateProfileString(Section, Key, "", temp,
255, this.path);
return temp.ToString();
}
}
}
A few applications I've worked on involve local databases and we used that database file to hold our settings too. It works, but something it just feels kinda wrong and it can be a problem if you would like to be able to modify a setting when your application is not running. (To bypass a crash or something alike.)
Most often I will just use an XML file stored in the Environment.SpecialFolder.ApplicationData folder and use a DataContractSerializer. It allows you to do some versioning, but most importantly, it's able to load and save XML files when certain properties on your settings object are new or non-existing. It won't crash the application and when you save your settings again it'll just save the latest version. I think it's clean, mean and simple.
Related
I've written a SaveLoad class, which contains a Savegame class that has a bunch of ints, doubles, bools but also more complex things like an array of self-written class objects.
That savegame object is being created, serialized and AES encrypted on save and vice versa on load - so far, so good.
The problem I'm facing now is that if there are new variables (in a newer version of the game) that have to be stored and loaded, the game crashes on load, because the new variables can't be loaded correctly (because they are not contained in the old save file). E.g. ints and doubles contain the default 0 while an array is not initialized, thus null.
My current "solution": For each variable that is being loaded, check if it doesn't contain a specific value (which I set in the Savegame class).
For example: In Savegame I set
public int myInt = int.MinValue;
and when loading, I check:
if(savegame.myInt != int.MinValue){
//load successful
}else{
//load failed
};
This works so far for int and double, but once I hit the first bool, I realized, that for every variable I have to find a value that makes "no sense"(not reachable usually), thus was a failed load. => Shitty method for bools.
I could now go ahead and convert all bools to int, but this is getting ugly...
There must be a cleaner and/or smarter solution to this. Maybe some sort of savegame migrator? If there is a well done, free plugin for this, that would also be fine for me, but I'd prefer a code-solution, which may also be more helpful for other people with a similar problem.
Thanks in advance! :)
Your issue is poor implementation.
If you are going to be having changes like this, you should be following Extend, Deprecate, Delete (EDD).
In this case, you should be implementing new properties/fields as nullables until you can go through and data repair your old save files. This way, you can check first if the loaded field is null or has a value. If it has a value, you're good to go, if it's null, you don't have a value, you need to handle that some way.
e.g.
/*We deprecate the old one by marking it obsolete*/
[Obsolete("Use NewSaveGameFile instead")]
public class OldSaveGameFile
{
public int SomeInt { get; set; }
}
/*We extend by creating a new class with old one's fields*/
/*and the new one's fields as nullables*/
public class NewSaveGameFile
{
public int SomeInt { get; set; }
public bool? SomeNullableBool { get; set; }
}
public class FileLoader
{
public SavedGame LoadMyFile()
{
NewSaveGameFile newFile = GetFileFromDatabase(); // Code to load the file
if (newFile.SomeNullableBool.HasValue)
{
// You're good to go
}
else
{
// It's missing this property, so set it to a default value and save it
}
}
}
Then once everything has been data repaired, you can fully migrate to the NewSaveGameFile and remove the nullables (this would be the delete step)
So one solution would be to store the version of the save file system in the save file itself. So a property called version.
Then when initially opening the file, you can call the correct method to load the save game. It could be a different method, an interface which gets versioned, different classes, etc but then you would require one of these for each save file version you have.
After loading it in file's version, you could then code migration objects/methods that would populate the default values as it becomes a newer version in memory. Similar to your checks above, but you'd need to know which properties/values need to be set between each version and apply the default. This would give you the ability to migrate forward to each version of the save file, so a really old save could be updated to the newest version available.
I'm facing the same problem and trying to build a sustainable solution. Ideally someone should be able to open the game in 10 years and still access their save, even if the game has changed substantially.
I'm having a hard time finding a library that does this for me, so I may build my own (please let me know if you know of one!)
The way that changing schemas is generally handled in the world of web-engineering is through migrations-- if an old version of a file is found, we run it through sequential schema migrations until it's up-to-date.
I can think of two ways to do this:
Either you could save all saved files to the cloud, say, in MongoDB, then change their save data for them whenever they make updates or
You need to run old save data through standardized migrations on the client when they attempt to load an old version of the save file
If I wanted to make the client update stale saved states then, every time I need to change the structure of the save file (on a game that's been released):
Create a new SavablePlayerData0_0_0 where 0_0_0 is using semantic versioning
Make sure every SavablePlayerData includes public string version="0_0_0"
We'll maintain static Dictionary<string, SavedPlayerData> versionToType = {"0_0_0": typeof(SavablePlayerData0_0_0)} and a static string currentSavedDataVersion
We'll also maintain a list of migration methods which we NEVER get rid of, something like:
Something like
public SavablePlayerData0_0_1 Migration_0_0_0_to_next(SavablePlayerData0_0_0 oldFile)
{
return new SavablePlayerData0_0_1(attrA: oldFile.attrA, attrB: someDefault);
}
Then you'd figure out which version they were on from the file version, the run their save state through sequential migrations until it matches the latest, valid state.
Something like (total pseudocode)
public NewSavedDataVersion MigrateToCurrent(PrevSavedDataVersion savedData)
{
nextSavedData = MigrationManager.migrationDict[GetVersion(savedData)]
if (GetVersion(nextSavedData) != MigrationManager.currentVersion) {
return MigrateToCurrent(nextSavedData, /* You'd keep a counter to look up the next one */)
}
}
Finally, you'd want to make sure you use a type alias and [Obsolete] to quickly shift over your codebase to the new save version
It might all-in-all be easier to just work with save-file-in-the-cloud so you can control migration. If you do this, then when a user tries to open the game with an older version, you must block them and force them to update the game to match the saved version stored in the cloud.
I am currently involved in writing an ASP.NET MVC 4 web version (using the Razor view engine) of an existing (Delphi) desktop based software product which at present allows customers (businesses) to completely customise all of the text in their instance of the application, both to localise it and to customise it to their specific environments.
For example the terms-
My tasks
Products
Workflows
Designs
Might all be changed to individual terms used within the business.
At present this customisation is simply done within the text strings which are stored within the application database, and compared and loaded on every form load in the Delphi database. I.e. every string on the form is compared with the database English strings and a replacement based on the selected locale is rendered on the form if available. I don't feel this is either scalable or especially performant.
I am also not personally comfortable with the idea of customisation happening within the localization method, that every string in the application can be changed by the end customer - it can lead to support issues in terms of consistency in text, and confusion where instructions are incorrectly changed or not kept up to date. There are lots of strings within an application that probably should not be changed beyond localizing them to the locale of the user - local language and/or formatting conventions.
I personally would rather stick with the ASP.NET APIs and conventions in localizing the web version of the application, using RESX resource files and resource keys rather than string matching. This is much more flexible than string matching where strings may have different contexts or cases and cannot simply be changed en-mass (there many English words which may have different meanings in different contexts, and may not map to the same set of meanings in other languages), crucially avoids round trips to the database to fetch the strings needed to fetch the page and also allows for ease of translation with a great set of tools around the standard RESX files. It also means no custom implementation is needed to maintain or document for future developers.
This does however give a problem of how we cope with these custom terms.
I'm currently thinking that we should have a separate RESX file for these terms, which lists defaults for the given locale. I'd then create a new database table which will be something like
CREATE TABLE [dbo].[WEB_CUSTOM_TERMS]
(
[TERM_ID] int identity primary key,
[COMPANY_ID] int NOT NULL, -- Present for legacy reasons
[LOCALE] varchar(8) NOT NULL,
[TERM_KEY] varchar(40) NOT NULL,
[TERM] nvarchar(50) -- Intentionally short, this is to be used for single words or short phrases
);
This can potentially read into a Dictionary<string, string> when needed and cached by IIS to provide lookup without the delay in connecting to the SQL server and conducting the query.
public static class DatabaseTerms
{
private static string DictionaryKey
{
get { return string.Format("CustomTermsDictionary-{0}", UserCulture); }
}
private static string UserCulture
{
get { return System.Threading.Thread.CurrentThread.CurrentCulture.Name; }
}
public static Dictionary<string, string> TermsDictionary
{
get
{
if (HttpContext.Current.Cache[DictionaryKey] != null)
{
var databaseTerms = HttpContext.Current.Cache[DictionaryKey] as Dictionary<string, string>;
if (databaseTerms != null)
{
return databaseTerms;
}
}
var membershipProvider = Membership.Provider as CustomMembershipProvider;
int? companyId = null;
if (membershipProvider != null)
{
companyId = CustomMembershipProvider.CompanyId;
}
using (var context = new VisionEntities())
{
var databaseTerms = (from term in context.CustomTerms
where (companyId == null || term.CompanyId == companyId) &&
(term.Locale == UserCulture)
orderby term.Key
select term).ToDictionary(t => t.Key, t => t.Text);
HttpContext.Current.Cache.Insert(DictionaryKey, databaseTerms, null, DateTime.MaxValue,
new TimeSpan(0, 30, 0), CacheItemPriority.BelowNormal, null);
return databaseTerms;
}
}
set
{
if (HttpContext.Current.Cache[DictionaryKey] != null)
{
HttpContext.Current.Cache.Remove(DictionaryKey);
}
HttpContext.Current.Cache.Insert(DictionaryKey, value, null, DateTime.Now.AddHours(8),
new TimeSpan(0, 30, 0), CacheItemPriority.BelowNormal, null);
}
}
}
I can then have a class which exposes public properties, returning a string based on either this dictionary value or the value in the RESX file - whichever is not null. Something like-
public static class CustomTerm
{
public static string Product
{
get
{
return (DatabaseTerms.TermsDictionary.ContainsKey("Product") ?
DatabaseTerms.TermsDictionary["Product"] : CustomTermsResources.Product);
}
}
}
These can then be added to larger localised strings using string formatting if required, or used by themselves as labels for menus etc.
The main disadvantage of this approach is the need to anticipate in advance which terms the end customers may wish to customise, but I do feel this might present the best of both worlds.
Does this seem like a workable approach and how have other devs approached this problem?
Thanks in advance.
I once designed an MVC application, whereby any string could be changed. In my case it was to handle other languages, but conceivably you could change anything just for aesthetic purposes. That and there is potential for the system to be marketed to other shops, and they may well call the same things different name (You say "Deferred Payment", I say "Lease Payment", etc.)
Warning: This solution is not about globalization and localization (e.g. left-to-right, word/verb ordering - it only needed to do what it did!)
It also considered the possibility of American English (en-US) vs British English (en-GB) vs Australian English (en-AU).
In the end, A Locale table was created in the database:
_id _localeName _idRoot
---------------------------
1 en-GB null
2 en-US 1
3 en-AU 2
Note how US and AU effectively have en-GB as their parent. en-GB therefore had every conceivably string that can be used in the application, in our translation table:
_id _idCulture _from _to
--------------------------------------
1 1 msgyes Yes
2 1 msgno No
3 1 msgcolour Colour
4 2 msgcolour Color
Now, during application initalisation, there was a config flag that specified the culture, which in my case happened to be en-AU. The system looks up the culture tree (en-AU derives from en-GB), and loads all the translations bottom up in to a dictionary cache. Therefore any en-AU specific translations overwrote the GB ones.
So, to describe it in your case - you'd have ALL translations in your database anyway, and that's your default setup. When the customer wishes to customise the text, they basically get a new node (or a derived culture in my example), and you build your cache again. Any terms they customised override the defaults. You no longer have to worry about what terms were done, it just works.
We have a similar setup in our application, we allow certain modules to have a custom names to fit the customers brand.
the first step to this solution is we know our client context at runtime and we stuff it into the HttpContext.Items.
For those items that can be customized, we introduced resource file containing the base keys. If the enterprise wants it customized we add a prefix in front of the key name (ie Client_key)
At once all this is in place its a simple coalesce to fetch the customized or default value.
Resx file snippet
<data name="TotalLeads" xml:space="preserve">
<value>Total Leads</value>
</data>
<data name="Client_TotalLeads" xml:space="preserve">
<value>Total Prospects</value>
</data>
Class to handle switch between custom and base resources
public static class CustomEnterpriseResource
{
public static string GetString(string key)
{
return GetString(key, Thread.CurrentThread.CurrentUICulture);
}
public static string GetString(string key, string languageCode)
{
return GetString(key, new CultureInfo(languageCode));
}
public static string GetString(string key, CultureInfo cultureInfo)
{
var customKey = ((EnterpriseContext)HttpContext.Current.Items[EnterpriseContext.EnterpriseContextKey]).ResourcePrefix + key;
return Resources.Enterprise.ResourceManager.GetString(customKey, cultureInfo)
?? Resources.Enterprise.ResourceManager.GetString(key, cultureInfo);
}
}
Also to assist in the views we create a html helper for this.
public static class EnterpriseResourceHelper
{
/// <summary>
/// Gets a customizable resource
/// </summary>
/// <param name="helper">htmlHelper</param>
/// <param name="key">Key of the resource</param>
/// <returns>Either enterprise customized resource or base resource for current culture.</returns>
public static string EnterpriseResource(this HtmlHelper helper, string key)
{
return CustomEnterpriseResource.GetString(key);
}
}
The requirement you have is not very common. I have worked in projects where localization is done purely using satellite assemblies and in projects where localization is done purely using database tables. In .NET, the recommended approach is RESX files compiled into satellite assemblies. It is a real good approach if you adopt it fully.
Your requirements are some where in between. Though the approach you plan to take, at this point sounds good on paper, I have a feeling over the course of time, maintenance will be difficult, since some of the strings will be in RESX and some will be in database. Even if the distribution is 90% - 10%, people will have difficulty figuring out where it takes the strings when you have all the strings loaded up in production. You will get queries from your users why a particular string is not showing up correctly and at that time it can get difficult for a developer (other than you) to figure out. You will be the best judge for your needs but I believe either you embrace RESX approach fully (which is not possible in your case) or go the full database route. If I have every thing in tables, all I need to do is to run a query for a given profile and I will see all the strings. This will be easier to support.
Even with database, you can follow a RESX-style approach of storing the full string against a key for a culture. The older approach of storing word by word is definitely not a good solution and will not work for different languages, since only sentences can be translated and not individual words. Your idea of caching is definitely needed for performance. So, basically having every thing in a bunch of tables, caching the same in memory and pulling the strings from cache based on the current culture is something I will go for. Of course, my opinion is based on what I could understand by reading your question :).
Also, check this out.
Very interesting question, thanks for bringing it up.
I have localized applications in very different ways, and your case is very specific. Let's start from the fact that everything comes down to localizing the labels/titles of the UI. Therefore, these elements must become localizable. On many platforms (such as WinForms, ASP.NET) they are localizable by design, and all it takes is extending the resource management model. I would say, this is the most natural way of localization if you are writing for such a platform.
In case of ASP.NET MVC, even though it's built on top of ASP.NET engine, we are not recommended to use the ASP.NET's server side tags and therefore the solution does not work. Why I provided it above is to give the clarity to my solution which I'm describing below.
Step 1 - Modularity
All labels and titles are part of some particular screen of the application. Since the screen is what groups them, I often use it for this exact purpose when describing localization resources. BTW, this is why we have one resx file per screen for the applications. So, we are following the consistent standard here.
To express modularity, define classes that correspond to each screen, and have properties defined on it that correspond to each localizable label or title on the screen.
Pseudo example:
class ProductPageResources
{
public string PageTitle { get; set; }
public string ProductNameLabel { get; set; }
}
Step 2 - Localization
When designing your application screens, stick to the modular resource classes defined above. Use localized strings from the modular resource class to display the labels and titles. If there's a need to add a label to the screen, don't forget to add a new property to the modular resource class too. Since it's ASP.NET MVC, we need to pass the resources alongside with the model. Conceptually it would not be necessary, but doing so gives us the flexibility to replace the resource implementation in the future (e.g. from MS SQL to some other source).
Usage example:
#{
ViewBag.Title = string.format(Model.Resources.PageTitle, Model.Product.Name);
}
...
<label>#Model.Resources.ProductNameLabel</label>
Note that the resource class property returns the localized string for the current culture, or the fallback value (described below) if not found. For the default value to appear as a value, I prepare the resource object by iterating the properties and assigning default values to them if they are empty (because the override was not found).
Step 3 - Customization
[Very nice and descriptive term you've got here, so I will use it.]
I personally don't think that the resource management should be data-driven. Main reason is that it's not dynamic enough. Just recall, that we have modular classes, and we start adding properties to it when we need to display something new on the screen. On the other hand, if you add something to the database, it's not appearing on the screen just so.
Therefore, we have a strongly-typed localization here, and it's very natural way of localizing things. The rest comes from this conclusion.
On your customization/resource administration screen you can use reflection to detect all the modular resource classes and display their properties on the screen for customization. To find the resource classes, you can put them under the same namespace, or you could mark them with some attributes to easier find them in the assembly. Either way works.
To make the modular resource class more display-friendly, you can use attributes to assign descriptions that should display instead of their Pascal-Case names on the screen.
So, our modular resource class becomes something like this:
[Description("Product Page")]
class ProductPageResources
{
[Description("Page Title")]
[DefaultValue("Product Details: {0}")
public string PageTitle { get; set; }
[Description("Product Name (label)")]
[DefaultValue("Name:")]
public string ProductNameLabel { get; set; }
}
Basically, on the customization screen we will see default values for Product Page, and each available localized value. For the last part, you can enumerate all the active cultures of the application and extract the value from the property again. Alternatively, you can use some other way depending on the implementation.
This seems to be an extensive reflection, but after all, Visual Studio does something very similar by allowing us to edit the resource files in the special editor. Bottom line is that you have a precisely working framework.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 12 months ago.
Improve this question
I'm writing an application that takes user data and stores it locally for use later. The application will be started and stopped fairly often, and I'd like to make it save/load the data on application start/end.
It'd be fairly straightforward if I used flat files, as the data doesn't really need to be secured (it'll only be stored on this PC). The options I believe are thus:
Flat files
XML
SQL DB
Flat files require a bit more effort to maintain (no built in classes like with XML), however I haven't used XML before, and SQL seems like overkill for this relatively easy task.
Are there any other avenues worth exploring? If not, which of these is the best solution?
Edit: To add a little more data to the problem, basically the only thing I'd like to store is a Dictionary that looks like this
Dictionary<string, List<Account>>
where Account is another custom type.
Would I serialize the dict as the xmlroot, and then the Account type as attributes?
Update 2:
So it's possible to serialize a dictionary. What makes it complicated is that the value for this dict is a generic itself, which is a list of complex data structures of type Account. Each Account is fairly simple, it's just a bunch of properties.
It is my understanding that the goal here is to try and end up with this:
<Username1>
<Account1>
<Data1>data1</Data1>
<Data2>data2</Data2>
</Account1>
</Username1>
<Username2>
<Account1>
<Data1>data1</Data1>
<Data2>data2</Data2>
</Account1>
<Account2>
<Data1>data1</Data1>
<Data2>data2</Data2>
</Account2>
</Username2>
As you can see the heirachy is
Username (string of dict) >
Account (each account in the List) >
Account data (ie class properties).
Obtaining this layout from a Dictionary<Username, List<Account>> is the tricky bit, and the essence of this question.
There are plenty of 'how to' responses here on serialisation, which is my fault since I didn't make it clearer early on, but now I'm looking for a definite solution.
I'd store the file as JSON. Since you're storing a dictionary which is just a name/value pair list then this is pretty much what json was designed for.
There a quite a few decent, free .NET json libraries - here's one but you can find a full list on the first link.
It really depends on what you're storing. If you're talking about structured data, then either XML or a very lightweight SQL RDBMS like SQLite or SQL Server Compact Edition will work well for you. The SQL solution becomes especially compelling if the data moves beyond a trivial size.
If you're storing large pieces of relatively unstructured data (binary objects like images, for example) then obviously neither a database nor XML solution are appropriate, but given your question I'm guessing it's more of the former than the latter.
All of the above are good answers, and generally solve the problem.
If you need an easy, free way to scale to millions of pieces of data, try out the ESENT Managed Interface project on GitHub or from NuGet.
ESENT is an embeddable database storage engine (ISAM) which is part of Windows. It provides reliable, transacted, concurrent, high-performance data storage with row-level locking, write-ahead logging and snapshot isolation. This is a managed wrapper for the ESENT Win32 API.
It has a PersistentDictionary object that is quite easy to use. Think of it as a Dictionary() object, but it is automatically loaded from and saved to disk without extra code.
For example:
/// <summary>
/// Ask the user for their first name and see if we remember
/// their last name.
/// </summary>
public static void Main()
{
PersistentDictionary<string, string> dictionary = new PersistentDictionary<string, string>("Names");
Console.WriteLine("What is your first name?");
string firstName = Console.ReadLine();
if (dictionary.ContainsKey(firstName))
{
Console.WriteLine("Welcome back {0} {1}", firstName, dictionary[firstName]);
}
else
{
Console.WriteLine("I don't know you, {0}. What is your last name?", firstName);
dictionary[firstName] = Console.ReadLine();
}
To answer George's question:
Supported Key Types
Only these types are supported as
dictionary keys:
Boolean Byte Int16 UInt16 Int32 UInt32
Int64 UInt64 Float
Double Guid DateTime TimeSpan String
Supported Value Types
Dictionary values can be any of the
key types, Nullable versions of the
key types, Uri, IPAddress or a
serializable structure. A structure is
only considered serializable if it
meets all these criteria:
• The structure is marked as
serializable • Every member of the
struct is either:
1. A primitive data type (e.g. Int32)
2. A String, Uri or IPAddress
3. A serializable structure.
Or, to put it another way, a
serializable structure cannot contain
any references to a class object. This
is done to preserve API consistency.
Adding an object to a
PersistentDictionary creates a copy of
the object though serialization.
Modifying the original object will not
modify the copy, which would lead to
confusing behavior. To avoid those
problems the PersistentDictionary will
only accept value types as values.
Can Be Serialized [Serializable] struct Good {
public DateTime? Received;
public string Name;
public Decimal Price;
public Uri Url; }
Can’t Be Serialized [Serializable] struct Bad {
public byte[] Data; // arrays aren’t supported
public Exception Error; // reference object }
XML is easy to use, via serialization. Use Isolated storage.
See also How to decide where to store per-user state? Registry? AppData? Isolated Storage?
public class UserDB
{
// actual data to be preserved for each user
public int A;
public string Z;
// metadata
public DateTime LastSaved;
public int eon;
private string dbpath;
public static UserDB Load(string path)
{
UserDB udb;
try
{
System.Xml.Serialization.XmlSerializer s=new System.Xml.Serialization.XmlSerializer(typeof(UserDB));
using(System.IO.StreamReader reader= System.IO.File.OpenText(path))
{
udb= (UserDB) s.Deserialize(reader);
}
}
catch
{
udb= new UserDB();
}
udb.dbpath= path;
return udb;
}
public void Save()
{
LastSaved= System.DateTime.Now;
eon++;
var s= new System.Xml.Serialization.XmlSerializer(typeof(UserDB));
var ns= new System.Xml.Serialization.XmlSerializerNamespaces();
ns.Add( "", "");
System.IO.StreamWriter writer= System.IO.File.CreateText(dbpath);
s.Serialize(writer, this, ns);
writer.Close();
}
}
I recommend XML reader/writer class for files because it is easily serialized.
Serialization in C#
Serialization (known as pickling in
python) is an easy way to convert an
object to a binary representation that
can then be e.g. written to disk or
sent over a wire.
It's useful e.g. for easy saving of settings to a file.
You can serialize your own classes if
you mark them with [Serializable]
attribute. This serializes all members
of a class, except those marked as
[NonSerialized].
The following is code to show you how to do this:
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
namespace ConfigTest
{ [ Serializable() ]
public class ConfigManager
{
private string windowTitle = "Corp";
private string printTitle = "Inventory";
public string WindowTitle
{
get
{
return windowTitle;
}
set
{
windowTitle = value;
}
}
public string PrintTitle
{
get
{
return printTitle;
}
set
{
printTitle = value;
}
}
}
}
You then, in maybe a ConfigForm, call your ConfigManager class and Serialize it!
public ConfigForm()
{
InitializeComponent();
cm = new ConfigManager();
ser = new XmlSerializer(typeof(ConfigManager));
LoadConfig();
}
private void LoadConfig()
{
try
{
if (File.Exists(filepath))
{
FileStream fs = new FileStream(filepath, FileMode.Open);
cm = (ConfigManager)ser.Deserialize(fs);
fs.Close();
}
else
{
MessageBox.Show("Could not find User Configuration File\n\nCreating new file...", "User Config Not Found");
FileStream fs = new FileStream(filepath, FileMode.CreateNew);
TextWriter tw = new StreamWriter(fs);
ser.Serialize(tw, cm);
tw.Close();
fs.Close();
}
setupControlsFromConfig();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
After it has been serialized, you can then call the parameters of your config file using cm.WindowTitle, etc.
If your collection gets too big, I have found that Xml serialization gets quite slow. Another option to serialize your dictionary would be "roll your own" using a BinaryReader and BinaryWriter.
Here's some sample code just to get you started. You can make these generic extension methods to handle any type of Dictionary, and it works quite well, but is too verbose to post here.
class Account
{
public string AccountName { get; set; }
public int AccountNumber { get; set; }
internal void Serialize(BinaryWriter bw)
{
// Add logic to serialize everything you need here
// Keep in synch with Deserialize
bw.Write(AccountName);
bw.Write(AccountNumber);
}
internal void Deserialize(BinaryReader br)
{
// Add logic to deserialize everythin you need here,
// Keep in synch with Serialize
AccountName = br.ReadString();
AccountNumber = br.ReadInt32();
}
}
class Program
{
static void Serialize(string OutputFile)
{
// Write to disk
using (Stream stream = File.Open(OutputFile, FileMode.Create))
{
BinaryWriter bw = new BinaryWriter(stream);
// Save number of entries
bw.Write(accounts.Count);
foreach (KeyValuePair<string, List<Account>> accountKvp in accounts)
{
// Save each key/value pair
bw.Write(accountKvp.Key);
bw.Write(accountKvp.Value.Count);
foreach (Account account in accountKvp.Value)
{
account.Serialize(bw);
}
}
}
}
static void Deserialize(string InputFile)
{
accounts.Clear();
// Read from disk
using (Stream stream = File.Open(InputFile, FileMode.Open))
{
BinaryReader br = new BinaryReader(stream);
int entryCount = br.ReadInt32();
for (int entries = 0; entries < entryCount; entries++)
{
// Read in the key-value pairs
string key = br.ReadString();
int accountCount = br.ReadInt32();
List<Account> accountList = new List<Account>();
for (int i = 0; i < accountCount; i++)
{
Account account = new Account();
account.Deserialize(br);
accountList.Add(account);
}
accounts.Add(key, accountList);
}
}
}
static Dictionary<string, List<Account>> accounts = new Dictionary<string, List<Account>>();
static void Main(string[] args)
{
string accountName = "Bob";
List<Account> newAccounts = new List<Account>();
newAccounts.Add(AddAccount("A", 1));
newAccounts.Add(AddAccount("B", 2));
newAccounts.Add(AddAccount("C", 3));
accounts.Add(accountName, newAccounts);
accountName = "Tom";
newAccounts = new List<Account>();
newAccounts.Add(AddAccount("A1", 11));
newAccounts.Add(AddAccount("B1", 22));
newAccounts.Add(AddAccount("C1", 33));
accounts.Add(accountName, newAccounts);
string saveFile = #"C:\accounts.bin";
Serialize(saveFile);
// clear it out to prove it works
accounts.Clear();
Deserialize(saveFile);
}
static Account AddAccount(string AccountName, int AccountNumber)
{
Account account = new Account();
account.AccountName = AccountName;
account.AccountNumber = AccountNumber;
return account;
}
}
A fourth option to those you mention are binary files. Although that sounds arcane and difficult, it's really easy with the serialization API in .NET.
Whether you choose binary or XML files, you can use the same serialization API, although you would use different serializers.
To binary serialize a class, it must be marked with the [Serializable] attribute or implement ISerializable.
You can do something similar with XML, although there the interface is called IXmlSerializable, and the attributes are [XmlRoot] and other attributes in the System.Xml.Serialization namespace.
If you want to use a relational database, SQL Server Compact Edition is free and very lightweight and based on a single file.
Just finished coding data storage for my current project. Here is my 5 cents.
I started with binary serialization. It was slow (about 30 sec for load of 100,000 objects) and it was creating a pretty big file on the disk as well. However, it took me a few lines of code to implement and I got my all storage needs covered.
To get better performance I moved on custom serialization. Found FastSerialization framework by Tim Haynes on Code Project. Indeed it is a few times faster (got 12 sec for load, 8 sec for save, 100K records) and it takes less disk space. The framework is built on the technique outlined by GalacticJello in a previous post.
Then I moved to SQLite and was able to get 2 sometimes 3 times faster performance – 6 sec for load and 4 sec for save, 100K records. It includes parsing ADO.NET tables to application types. It also gave me much smaller file on the disk. This article explains how to get best performance out of ADO.NET: http://sqlite.phxsoftware.com/forums/t/134.aspx. Generating INSERT statements is a very bad idea. You can guess how I came to know about that. :) Indeed, SQLite implementation took me quite a bit of time plus careful measurement of time taking by pretty much every line of the code.
The first thing I'd look at is a database. However, serialization is an option. If you go for binary serialization, then I would avoid BinaryFormatter - it has a tendency to get angry between versions if you change fields etc. Xml via XmlSerialzier would be fine, and can be side-by-side compatible (i.e. with the same class definitions) with protobuf-net if you want to try contract-based binary serialization (giving you a flat file serializer without any effort).
If your data is complex, high in quantity or you need to query it locally then object databases might be a valid option. I'd suggest looking at Db4o or Karvonite.
A lot of the answers in this thread attempt to overengineer the solution. If I'm correct, you just want to store user settings.
Use an .ini file or App.Config file for this.
If I'm wrong, and you are storing data that is more than just settings, use a flat text file in csv format. These are fast and easy without the overhead of XML. Folks like to poo poo these since they aren't as elegant, don't scale nicely and don't look as good on a resume, but it might be the best solution for you depending on what you need.
Without knowing what your data looks like, i.e. the complexity, size, etc...XML is easy to maintain and easily accessible. I would NOT use an Access database, and flat files are more difficult to maintain over the long haul, particularly if you are dealing with more than one data field/element in your file.
I deal with large flat-file data feeds in good quantities daily, and even though an extreme example, flat-file data is much more difficult to maintain than the XML data feeds I process.
A simple example of loading XML data into a dataset using C#:
DataSet reportData = new DataSet();
reportData.ReadXml(fi.FullName);
You can also check out LINQ to XML as an option for querying the XML data...
HTH...
I have done several "stand alone" apps that have a local data store. I think the best thing to use would be SQL Server Compact Edition (formerly known as SQLAnywhere).
It's lightweight and free. Additionally, you can stick to writing a data access layer that is reusable in other projects plus if the app ever needs to scale to something bigger like full blown SQL server, you only need to change the connection string.
Depending on the compelexity of your Account object, I would recomend either XML or Flat file.
If there are just a couple of values to store for each account, you could store them on a properties file, like this:
account.1.somekey=Some value
account.1.someotherkey=Some other value
account.1.somedate=2009-12-21
account.2.somekey=Some value 2
account.2.someotherkey=Some other value 2
... and so forth. Reading from a properties file should be easy, as it maps directly to a string dictionary.
As to where to store this file, the best choise would be to store into AppData folder, inside a subfolder for your program. This is a location where current users will always have access to write, and it's kept safe from other users by the OS itself.
My first inclination is an access database. The .mdb files are stored locally, and can be encrypted if that is deemed necessary. Though XML or JSON would also work for many scenarios. Flat files I would only use for read only, non-search (forward read only) information. I tend to prefer csv format to set width.
It depends on the amount of data you are looking to store. In reality there's no difference between flat files and XML. XML would probably be preferable since it provides a structure to the document. In practice,
The last option, and a lot of applications use now is the Windows Registry. I don't personally recommend it (Registry Bloat, Corruption, other potential issues), but it is an option.
If you go the binary serialization route, Consider the speed at which a particular member of the datum needs to be accessed. If it is only a small collection, loading the whole file will make sense, but if it will be large, you might also consider an index file.
Tracking Account Properties/fields that are located at a specific address within the file can help you speed up access time, especially if you optimize that index file based on key usage. (possibly even when you write to disk.)
Keep it simple - as you said, a flat file is sufficient. Use a flat file.
This is assuming that you have analyzed your requirements correctly. I would skip the serializing as XML step, overkill for a simple dictionary. Same thing for a database.
In my experience in most cases JSON in a file is enough (mostly you need to store an array or an object or just a single number or string). I rarely need SQLite (which needs more time for setting it up and using it, most of the times it's overkill).
I have an idea that reading values from config files instead of using hard code values, but I'm not sure it is a good practice or not.
First I created a utility class:
public class ConfigValues
{
public static int Read(string key, int defaultValue){....}
public static string Read(string key, string defaultValue){....}
public static bool Read(string key, bool defaultValue){....}
...
}
The Read function tries to read value for the given key. If the key doesnot exist or the value has bad format, it returns the default value. And I'm going to use this class like:
public class MyClass
{
private int _age = ConfigValues.Read("Com.MyClass.Age", 0);
...
}
So that, we can make almost all variables in the application customizable.
Will it be a good practice?
Please comment it for free.
People who think you should make things configurable:
Some of the other answers
http://www.ebizq.net/blogs/decision_management/2007/04/dont_softcode_use_business_rul.php
Many good software development theories (I don't have links handy).
People who think differently:
http://ayende.com/Blog/archive/2008/08/21/Enabling-change-by-hard-coding-everything-the-smart-way.aspx (And the rest of his entries)
http://thedailywtf.com/Articles/Soft_Coding.aspx
http://benbro.com/blog/on-configuration/
http://jeffreypalermo.com/blog/hardcoding-considered-harmful-or-is-it/
The answer comes down to your requirements: why are you setting this value here?
Is it something that different users will want set differently? => config file.
Is it just a default value to be changed later? => Hardcode it.
Is it something which affects operational use of the program (i.e. default homepage for browser)? => Config file.
Is it something which might have complex impacts on various parts of the program? ... Answer depends on your userbase.
Etc. It's not a simple yes-it's-good or no-it's-bad answer.
Configuration files are always a good idea.
Think of the INI files, for example.
It would be immensely useful to introduce a version numbering scheme in your config files.
So you know what values to expect in a file and when to look for defaults when these are not around. You might have hardcoded defaults to be used when the configurations are missing from the config file.
This gives you flexibility and fallback.
Also decide if you will be updating the file from your application.
If so, you need to be sure it can manage the format of the file.
You might want to restrict the format beforehand to make life simpler.
You could have CSV files or "name=value" INI style files.
Keep it simple for your code and the user who will edit them.
Configuration file also allow you to update the values without doing another build to change a single value. This can be useful to have one build for all environments, with different configuration values (log levels, user names, etc.). Also, if you cache the values and periodically update from the file, it allows you to make changes on the fly while the application is still running. This may be overkill in some situations, but it can be very helpful in debugging certain issues.
Do:
Create a new C# project
Menu->Project->[Project name] Properties
Open Settings pane
Create your settings (it's strongly typed)
Usage:
class Program
{
static void Main(string[] args)
{
int setting1 = Properties.Settings.Default.Setting1;
Properties.Settings.Default.Setting1 = 43534;
Proerties.Settings.Default.Save();
}
}
Note that Properties is a namespace and you can import it so you can use Settings.Default.Setting1.
Here is a more detailed link.
If you use a windows forms application, you could change the Program.cs file to save all the changed settings when leaving application:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
//Add this line
Application.ApplicationExit +=
new EventHandler(Application_ApplicationExit);
Application.Run(new Form1());
}
//and this method:
static void Application_ApplicationExit(object sender, EventArgs e)
{
Properties.Settings.Default.Save();
}
}
I have a console application that I am rebuilding from C to C#. This application has to be able to support the legacy method of storing information like parameters from a command-line and parameters from a file (called the system parameters) that customize each run. The system parameters file is in plain-text with a simple key-value structure.
My questions are:
Should I combine these different parameters into a single Configuration object?
How would I call this configuration object from the code to store parameters?
How would I call this configuration object from the code to retrieve parameters?
Should this object be strongly-typed?
I will need access to this structure from a lot of different places in the code. What is the most elegant way to retrieve the values in the object without passing the object itself around everywhere?
I have a feeling that it should be a single, strongly-typed object and that it should be an instantiated object that is retrieved from a repository with a static retrieval method however I really want validation of this method.
I would use a single configuration object like the following:
using System;
using System.IO;
using System.Reflection;
public sealed class Setting {
public static int FrameMax { get; set; }
public static string VideoDir { get; set; }
static readonly string SETTINGS = "Settings.ini";
static readonly Setting instance = new Setting();
Setting() {}
static Setting() {
string property = "";
string[] settings = File.ReadAllLines(SETTINGS);
foreach (string s in settings)
try {
string[] split = s.Split(new char[] { ':' }, 2);
if (split.Length != 2)
continue;
property = split[0].Trim();
string value = split[1].Trim();
PropertyInfo propInfo = instance.GetType().GetProperty(property);
switch (propInfo.PropertyType.Name) {
case "Int32":
propInfo.SetValue(null, Convert.ToInt32(value), null);
break;
case "String":
propInfo.SetValue(null, value, null);
break;
}
} catch {
throw new Exception("Invalid setting '" + property + "'");
}
}
}
Since this is a singleton, it will create one and only one instance of itself the first time a public static property is referenced from the Setting object.
When the object is created, it reads from the Settings.ini file. The settings file is a plain-text file with a simple key : value structure that might look like this:
FrameMax : 12
VideoDir : C:\Videos\Best
The object uses reflection to discover each property and to store its initial value. In this example, two properties have been defined:
public static int FrameMax { get; set; }
public static string VideoDir { get; set; }
The code as written handles Int32 and String types. By adding additional case statements to the switch statement, you could easily add support for types like Float and Decimal.
To change a setting, you would use something like:
Setting.FrameMax = 5;
To retrieve a setting, you would use something like:
if (Setting.FrameMax > 10) ...
You'll notice that all the properties are strongly-typed. Also, you don't have to pass the Setting object around, as all the Setting properties are static and always available everywhere.
I hope this idea is helpful.
I like using Settings. These can be generated automatically either by creating a settings file using the Add New File dialog box, or by adding a default settings file from project properties.
Each setting may be in user or application scope, which controls whether or not the user can change them or they are restricted to their default values. They are easily saved with the Save() method and loaded automatically into the static Default property.
This class seems to be for application or user-based settings. I'm looking for per-run settings. Would you still recommend using this class in that case? – x97mdr
Yes. If you have both user/application based settings and per-run settings you should use two different classes - the normal (saved) settings and the per-run settings.
As long as you don't save the per-run settings, you should be safe and settings are still quite easy to use. These are static settings though. If the same application run needs several instances - this is the wrong approach.
I find that whenever I have to deal with a legacy system, sticking with the old format almost always works best. Often times there are other people using the legacy formats for other tasks (like automation of the app, for example), so if you recode the way the application handles inputs, you might break other systems.
On the other hand, if you are pretty confident that you know all the people using the system, and they tell you that they don't care if you change these types of things, I would probably move everything to XML. Besides all the nice features of XML from an application point of view (like being in ASCII so it's easily modified by humans, being self-documenting, etc ...), XML is also time-saving, in that you don't have to write your own I/O or parser. There's already a wide variety of libraries out there, particularly in .NET 3.0/3.5, that do very well. (As you're moving to C#, I'm guessing you're already thinking along these lines :)
So ultimately, you'd have to base your decision on cost-to-implement: if you lower your cost of implementation by moving to XML or similar, make sure that you don't raise other people's cost of implementation to move to your new application framework.
Good luck!
XmlDocument - you can generate a class definition using XSD.exe