To give a bit of a background.
I have created an application that allows users to save settings and then recall the settings at a later date. To do this I have created some serializable objects. I have gotten this to work using the BinaryFormatter without much trouble.
Where I start to run into problems is when I upgrade the software and add new settings. Now my serializable objects do not match and so I have to update the files. I have done this successfully for a few versions. But to do this I try deserializing the file and if it throws an exception, I try with the next version. . .and then the next. . .and then the next. . . until I find the right one. Then I have to write conversion functions for each of old versions to convert it into the newest version. I did create a "revision" file as well, so I can just check up front what version they have and then upgrade it, but I still have to keep a lot of different "versions" alive and write the conversion functions for all of them. . . which seems inherently messy to me and prone to bloat later on down the line if I keep going this route.
There has to be a better way to do this, I just am not sure how.
Thanks
You need to write a serialization binder to resolve assemblies.
For settings, I use a Dictionary<string, byte[]> to save to file. I serialize the dictionary and all is well. When I add new settings, I provide a default setting if not found in the settings file.
Also, if you are adding fields to a serialized object, you can decorate with [Optional].
This is exactly what the Settings class is for. You define default values in your app.config, and then a user can change them and when you save, their changes will save to a location in their user profile. When you read them you'll just get the modified settings.
This link is for VS 2005, but it works exactly the same in VS 2012: http://msdn.microsoft.com/en-us/library/aa730869(v=vs.80).aspx
Found the link for VS2012: http://msdn.microsoft.com/EN-US/library/k4s6c3a0(v=VS.110,d=hv.2).aspx
XML format is for such cases. You will find the necessary old settings in very early version of settings file. And even the old version can handle XML settings created from newer version. It does not work "automatically" i.e. with method like Serialize/Deserialize, but writing conversion functions is not easier or faster.
actually this can be done by adding a [DefaultValue()] attribute to the newer properties on your settings objects - at least for XML serialization. I haven't attempted this using binary serialzation. For xml, this means that they are "optional" and the serialization will not break when loading old version of the files. You can find this attribute in the System.ComponentModel namespace as so;
class MySettings
{
public int MaxNumLogins { get; set; }
// specify the value to default to if it's not present in the serialized file...
[DefaultValue(0)]
public int CacheTimeoutMinutes { get; set; }
}
In addition to naming the fields as others have suggested this sort of thing just cries out for version numbers.
You could have a look at ProtoBuf-Net http://code.google.com/p/protobuf-net/wiki/GettingStarted if you are doing Binary because all these things are covered regarding versioning etc. It is also very compact. It also is actively developed and if you potentially have cross platform requirements if you use a .proto file you can also achieve that.
If you would like people to be possibly able to edit the settings (outside of your program) then you could use the XML* serialisation methods.
Related
I am developing an application using C# in .NET Framework 4.7.2. Originally I had some data that I thought would be useful to use .ini files for configuration purposes. I learned rather quickly that .ini files are out of vogue in .NET and that it would be prudent to use configuration files. That is, .settings files.
For some context for my application, these configuration data would refer to PLC program tag names that could be added/removed rather easily by a programmer without needing to expose the C# application (ie. If we wanted to add a tag to be read by our application, we can easily add it to the configuration file and the string value would be parsed by our Tag-Reading Extension).
What I have done is create a number of .settings files to allow for an easier way for me to organize the scope of certain blocks of tags. It may not be the most efficient way to do this but it works for me at the moment to do proof of concept. I may change this later but right now I'd like to keep it as is.
My question relates to how these .settings files work and if they are configurable themselves.
In the picture above, I see four headers. I've been calling them properties: "Name", "Type", "Scope", and "Value". I am able to refer to any of these properties using the SettingsProperty class (I think is this the correct classification for this object). In a few very specific cases I need to supply an additional property to a tag to give it special attention. Is this something possible in C# visual studio 2019 and if so, could someone please point me in the right direction to achieve this? I thought about perhaps using a List of Strings instead but would prefer from a usability standpoint to not do it this way if there is a way around that I simply haven't found yet. I haven't been successful in searching for how/if this is done or if this is even an appropriate way to solve this problem.
In short, in addition to "Name", "Type", "Scope" and "Value", I would like to add "TagType" (of type string) which I can set and get as needed from within my .settings files.
Thank you very much for any insight that can be offered!
EDIT: Typos
Here is an idea that you can store both value and TagType into Column Value. And use any one character to separate them, such as |.
Then you can access them by splitting the string.
string value = Properties.Settings.Default.Tag_1.Split('|')[0].Trim();
string tagtype = Properties.Settings.Default.Tag_1.Split('|')[1].Trim();
Console.WriteLine($"Value = {value}, TagType = {tagtype}");
Hope this can help you.
I'm serializing a class using a BinaryFormatter. When I open the created file in a texteditor, I can see that at the beginning, some attributes like namespace, version, cultureInfo, ... are written there. How can I read this version string out when deserializing this file again?
Thank you in advance!
You should take a look at this articles at MSDN:
Run-time Serialization, Part 1
Run-time Serialization, Part 2
Run-time Serialization, Part 3
The BinaryFormatter has two properties: Binder and SurrogateSelector.
With these you are able to interfere the serialization / deserialization process and access these informations. More informations about it can be found in the articles above.
You probably should read that part like a normal file (read and check bytes).
However, why would you be interested in that part? If you are, than it's best to add your own version attributes in the normal way as other data to be serialized and retrieve it the normal way (by deserialization like all other data).
Remark to your comment:
If this is the first time, you could write an 'updater', which reads the old file and transforms it with a new (so change the enum values). For the new serialization object, add a version (always, and update it for each version your publish). This case, you can always differ on changes. By making such an update function, you always can change older versions of data to newer versions. In this case (since you don't have a version), you can assume it is the old version.
The XML that i am currently working is directly formed using XML serializer (Serializing Class and its nested counter parts)
Also if there is an addition of a new Property is directly handled by the serializer but the problem comes when there is a deletion of property (value Type) or removal of and entire class or addition of class
I wish to read the old as well as the new XML files.... I cant seem to figure out how..
Process
Some ways
But i don't think these are good for a maintainable code
1) Make the custom XML parser (this will be less flexible as every time the change is done the parser has to be updated and hence tested again).
2) Use multiple Models then migrate from old to new (Taking essential components)
3) Export Old file and import the new file (This will also require another XML file and may b related to point 2)
4) Any other means (Please suggest)
I am not well versed with XML and its versioning.
Also is XML a good choice for this or Any other file type/DB that i can use in place of XML
Any help in this regard would be helpful.
In most ways, XmlSerializer already has pretty good version support built in. In most cases, if you add or remove elements it isn't a problem: extra (unexpected) data will be silently ignored - or put into the [XmlAnyElement] / [XmlAnyAttribute] member (if one) for round-trip. Any missing data just won't be initialized. The only noticeable problem is with sub-types, but adding and removing sub-types (or entire types) is going to be fairly fundamental to any serializer. One common option in the case of sub-types is: use a single model, but just don't remove any sub-types (adding sub-types is fine, assuming you don't need to be forwards compatible). However if this is not possible, the multiple models (model per revision) is not a bad approach.
I usually follow your solution "#2" where I namespace version my models (Myapp.Models.V1.MyModel), this way you can maintain backward compatibility with clients still using the older schema (or in your case, loading an older file).
As suggested in the comments, you can use a simple attribute on the root node to determine the version, and use either xmlreader, or even a simple regex on the first line of the file to read the version number.
As far as your second question, about file type/db, depending on your needs, I would highly recommend looking at a document database like MongoDB or RavenDB, as implementation is straightforward/simple, and does not require the use of an ORM tool like entity framework to handle proper separation of concerns. If you need something portable, in the cases such as desktop app "save file", SqlLite is a good file based databases, but you will likely want to use an ORM for mapping your model to your database.
Links:
MongoDB: http://www.mongodb.org/
RavenDB: http://ravendb.net/
Sqllite: http://www.sqlite.org/
I'm using C# .NET.
In my software I'm providing settings dialog through which user can set the application settings which I want to save to a file.
Requirements (typical):
Every class I defined uses some part of these settings. So, these should be global to all classes.
These should be loaded while software is started.
When ever user changes settings and clicks 'save'/'apply'. Current settings should change.
I am wondering what is the best way to do this? Also, what is the best way to save these settings to disk? I mean should I create a Settings class object and serializing it to 'settings.dat' or provide an structured file like XML/JSON
This is required for almost every other software. So, isn't there any design pattern for this?
EDIT:
Well, thats something that I didn't know. Its nice :). But say while user is using the software in the middle he changes the settings then all the other objects that are using these global Properties.Settings.Default.* should be changed. Is there any kind of notification mechanism? Some kind of event?
.Net projects already have the notion of Settings, scoped to either the user or the application, that will meet all of your requirements above. There are classes to read and write the settings. I would highly recommend that you look at these instead of rolling something up yourself.
Using Settings in C#
You can use Settings in a variety of project types, although in certain types of projects like ASP.Net projects, User-level settings may not be available.
The Settings class that comes with .Net is very handy, and I use it for most of my projects. The one gotcha to watch out for is that every new version of the application gets its own settings file, so make sure you have sensable defaults. All the settings will disappear whenever a new EXE is distributed.
Global state is very hard to deal with correctly, so I usually pass the relevant settings to the various objects in their constructors, or in properties. And I usually don't apply settings changes to those objects, since, in many cases, it's very hard for an object to deal with a changing setting intelligently. Rather, I just use the new settings for the new objects as they are created. If a setting needs to be applied immediately, then I just dump the old object and create a new one. It just depends on the details of the application.
If you have an Apply button on your settings screen, then I would recommend reloading and displaying all of the values after saving them. This way the display is sure to contain exactly what is actually saved. This could be important if any settings are parsed. I've had users enter a month and day combination into a particular field, and the format they used was different from what was expected, so the value saved was incorrect. By updating the screen after the Apply, these sorts of errors can be made obvious.
I hope this helps!
You and womp are both right:
You should create a Settings class that is a Facade over the .NET settings. That way you get the best of both worlds: the testability of a hand-rolled solution and the ease of implementation typically associated with Microsoft Silver Bullets.
I personally would go the Properties.Settings route. Add a Settings file to the Properties folder of your app. This lets you easily add items to the app.config file of your application. There is a built in .net class that you can use to read/write values found in the Settings file. You can then write a small wrapper class that encapsulates that functionality or simply use the built in .net one all over the place.
I personally would create a threadsafe Singleton class that uses the small wrapper class over the built in .net one. Yes it's some extra work but it's a small amount and gives you some great power in your app for the little bit of work.
Edit: Sorry forgot to include the MSDN settings link.
http://msdn.microsoft.com/en-us/library/aa730869(VS.80).aspx
As others have suggested using the Settings feature is the way to go. Settings also provide events when the they are updated so you can handle them and take necessary actions. You can also use the Upgrade method to move the settings from previous version into the newer one. Just make sure that you call it only once (probably from the installer).
Settings can also be bounded to the controls so you will not have to map them manually from controls to settings.
I've got a working app that serializes a document (of type IDocument) to disk. From there there's another app that I've made that can open that document (IDocument implements IPrintDocument) for viewing.
Let's assume that I've written an IDocument to disk, and then a week later a field gets added to the IDocument object. Both the program that writes the files and the one that opens them are updated with this new 'version' of IDocument. It will then break (I assume - haven't had a chance to check, I'm looking ahead here) when trying to open the previous IDocument version. Is there a known pattern that alleviates this kind of problem?
Yes - use a serialization mechanism which is tolerant to versioning.
Predictably enough, I'm going to suggest using Google's Protocol Buffers, for which there are at least two viable .NET implementations. So long as you're careful, Protocol Buffers are both backward and forward compatible - you can read a new message with old code and vice versa, and the old code will still be able to preserve the information it doesn't understand.
Another alternative is XML, whether using .NET's built-in XML serialization or not. The built-in serialization isn't particularly flexible in terms of versioning as far as I'm aware.
The .net built-in serialization is an option, but it does requires you to add place holders on the specific pieces that you want to extend in the future.
You add place holders for the extra elements/attributes like the following code:
[XmlAnyElement()]
public XmlElement[] ExtendedElements { get; set; }
[XmlAnyAttribute()]
public XmlAttribute[] ExtendedAttributes { get; set; }
By adding the above in the involved classes, you can effectively read a information saved that has extra elements/attributes, modify the normal properties that the software knows how to handle and save it. This allows for both backwards and forward compatibility. When adding a new field, just add the desired property.
Note that the above is limited to extend in the specified hooks.
Update: As Jon mentioned in the comment, the above will only work for xml serialization. As far as I know binary serialization doesn't support something similar. In binary serialization you can get both old/new version of the app to be able to read each other serialized info (.net 2.0+), but if you save it back you will loose the extra info the version doesn't handles.
Starting at .net 2.0 the de-serialization process ignores the extra data, if you combine that with optional fields you can effectively get both apps to read other version's formats. The problem is that the data isn't hold by the class like in the xml fields.
Some related links: http://msdn.microsoft.com/en-us/library/system.runtime.serialization.serializationbinder.aspx, http://msdn.microsoft.com/en-us/library/ms229752.aspx
If you don't want xml serialization, I would go with Jon's approach.
Ps. I am unaware if there is some good third party implementation, that we can access, that extends the binary serialization to hold and save the extra data.
Built in serialization should give you some minimal tolerance for version updates using the [OptionalField] attribute. But stuff can get tricky really fast so you better look at using a framework that solved these issues like Jons protobuffers etc...
Another couple of options would be to use an embedded DB like Sqlite for your document store. And manually (or using an ORM) map properties/fields in your object to columns in a table.
or
Use Lucene which will also give you fulltext search through your documents.