Saving data with object-based data source in winforms app - c#

I am building a C# winforms application which needs to read in data from multiple files and allow the user to view/edit the data. There will be a large amount of data, so the user needs to be able to save their changes, close the program, and resume their work later.
I am struggling with the best approach for retaining this data after the user exits the program. I've followed a tutorial for data binding to objects, but in this tutorial the data is hardcoded into the Form_Load event and the changes are lost when you exit the program. The author alludes to preferring to use an object-based data source instead of a database for data binding, but doesn't describe on how/if he saves data after the user exits.
Is there a way to store the data in the object-based data source between sessions, without setting up a local database or manually writing to some type of file? Or must I set up a local database in order to save data?

As users Jimi and bhmahler mentioned in the comments, the concept I was looking for was Object Serialization.
I created the following method to save my data:
private void Serialize()
{
Stream s = File.Open("data.txt", FileMode.Open);
BinaryFormatter b = new BinaryFormatter();
List<Airplane> data = new List<Airplane>();
foreach (var a in bsAirplanes) //bsAirplanes is the bindingsource object
{
data.Add((Airplane)a);
}
b.Serialize(s, data);
s.Close();
}
And this method to load saved data:
private void Deserialize()
{
Stream s = File.OpenRead("data.txt");
BinaryFormatter b = new BinaryFormatter();
List<Airplane> data = (List<Airplane>) b.Deserialize(s);
foreach(Airplane a in data)
{
bsAirplanes.Add(a);
}
s.Close();
}
I also had to mark the Airplane class with the [Serializable()] attribute.

Related

Serialize XML in C#

I'm basically trying to add podcasts to an XML file and eventually retrieve them from that same XML, using the data in different parts of the application.
I successfully write the data to the XML but every time I reboot the application (debug) and press the "submit" button the XML file resets to 0 entries.
The submit code:
PodcastList podList = new PodcastList();
private void btnAddPod_Click(object sender, EventArgs e)
{
var podUrl = txtAddPod.Text;
var podName = txtEnterName.Text;
podList.AddPod(podUrl, podName, 0);
Xml.SaveListData(podList.GetPodcastList(), "Podcasts.xml");
}
Save to XML:
public static void SaveListData(object obj, string filename)
{
var serializer = new XmlSerializer(obj.GetType());
using (var stream = new StreamWriter(filename))
{
serializer.Serialize(stream, obj);
}
}
I guess the applications creates a new XML-file every time I press submit and has fresh objects. What am I doing wrong? Cheers
XML files are not generally 'appended to' due to the need for opening and closing tags. As opposed to say, other types of text file like log files where appending makes a bit more sense.
When you call serializer.Serialize the whole file gets overwritten.
What your program needs to do is read in the already-existing XML file on startup and store that as a PodcastList(). Your program can then add to it (in memory) and save the whole list as a file.
PodcastList podList = new PodcastList(); is at the class level.
If you want to maintain the state , Reload the XML file Deserialize the file to PodcastList in the Constructor or at the time of the load, Then you will be able to retain and reuse the collection and rewrite the data back to XML file.

Caching posted data and fall-backs

I'm currently working on a project that has an external site posting xml data to a specified url on our site. My initial thoughts were to first of all save the xml data to a physical file on our server as a backup. I then insert the data into the cache and from then on, all requests for the data will be made to the cache instead of the physical file.
At the moment I have the following:
[HttpPost]
public void MyHandler()
{
// filePath = path to my xml file
// Delete the previous file
if (File.Exists(filePath))
File.Delete(filePath));
using (Stream output = File.OpenWrite(filePath))
using (Stream input = request.InputStream)
{
input.CopyTo(output);
}
// Deserialize and save the data to the cache
var xml = new XmlTextReader(filePath);
var serializer = new XmlSerializer(typeof(MyClass));
var myClass = (MyClass)serializer.Deserialize(xml);
HttpContext.Current.Cache.Insert(myKey,
myClass,
null,
myTimespan,
Cache.NoSlidingExpiration,
CacheItemPriority.Default, null);
}
The issue I have is that I'm always getting exceptions thrown because the file that I'm saving to 'is in use' when I try a second post to update the data.
A colleague suggested using a Mutex class just before I left work on the Friday so I wonder if that is the correct approach here?
Basically I'm just trying to sanity check that this is a good way of managing the data? I can see there's clearly an issue with how I'm writing the data to a file but aside from this, does my approach make sense?
Thanks

Load PDF from memory into telerik:RadPdfViewer

I have a PDF file stored in a database as a byte array.
I'm reading the PDF byte array from my database back into my application.
Now, I'm trying to display the PDF with the RadPdfViewer but it is not working.
Here is my code:
byte[] pdfAsByteArray= File.ReadAllBytes(#"C:\Users\Username\Desktop\Testfile.pdf");
//Save "pdfAsByteArray" into database
//...
//Load pdf from database into byte[] variable "pdfAsByteArray"
using (var memoryStream = new MemoryStream(pdfAsByteArray))
{
this.PdfViewer.DocumentSource = new PdfDocumentSource(memoryStream);
}
when I execute the application I just get an empty PdfViewer.
Question: How do I set the DocumentSource the right way?
Question: How do I dispose the stream? (note that using doesn't works)
Note: I wan't to avoid things like writing a temp file to disk
Edit:
I figured it out but I am not completely satisfied with this solution:
Not working:
using (var memoryStream = new MemoryStream(pdfAsByteArray))
{
this.PdfViewer.DocumentSource = new PdfDocumentSource(memoryStream);
}
Working:
var memoryStream = new MemoryStream(pdfAsByteArray);
this.PdfViewer.DocumentSource = new PdfDocumentSource(memoryStream);
I don't know how teleriks RadPdfViewer component works but I wan't to dispose the Stream.
From the Telerik documentation (particularly with regards to the "Caution" stating that this loading is done asynchronously), I believe this should work while still providing you a way to close the stream (not as cleanly as if you were able to use a using block, but still better than leaving it open):
//class variable
private MemoryStream _stream;
_stream = new MemoryStream(pdfAsByteArray);
var docSource = new PdfDocumentSource(memoryStream);
docSource.Loaded += (sender, args) => { if (_stream != null) _stream.Dispose();};
this.PdfViewer.DocumentSource = docSource;
I did this free-hand and don't have access to the Telerik API so the exact details of the Loaded event are not available to me.
EDIT
Here's the relevant details from documentation I found (emphasis mine):
The PdfDocumentSource loads the document asynchronously. If you want
to obtain a reference to the DocumentSource after you have imported a
document, you should use the Loaded event of the PdfDocumentSource
object to obtain the loaded document. This is also a convenient method
that can be used to close the stream if you are loading a PDF from a
stream.
You need to implement PdfDocumentSource Loaded event. This is when the stream gets loaded and used up, and can be closed / disposed at that time.
Another method I've used is:
this.PdfViewer.PdfjsProcessingSettings.FileSettings.Data = Convert.ToBase64String(File.ReadAllBytes(#"C:\Users\Username\Desktop\Testfile.pdf"));

C# - Save Variables of a Program

I'm making a game now in C# (which is a Console Application) and its variables need to be saved.
I've tried using Settings but there's a big problem about it: If the file name is changed or the file is transferred to somewhere else, the Settings are lost.
So what is a good alternative to Settings for saving variables and retrieving them later in the application?
EDIT: I'd like to save the variables to a text file and retrieve it later, is it possible? If yes, then how?
And please don't suggest online servers, because I'm working on a singleplayer game without keeping tracks of the players whatsoever.
One simple way to store data of a fixed type is serialization with the BinaryFormatter class.
See the MSDN documentation for Binary Formatter. I've copied some of the relevant code here.
using System;
using System.IO;
using System.Collections;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
void SaveData()
{
// Create a hashtable of values that will eventually be serialized.
Hashtable addresses = new Hashtable();
addresses.Add("Jeff", "123 Main Street, Redmond, WA 98052");
addresses.Add("Fred", "987 Pine Road, Phila., PA 19116");
addresses.Add("Mary", "PO Box 112233, Palo Alto, CA 94301");
// To serialize the hashtable and its key/value pairs,
// you must first open a stream for writing.
// In this case, use a file stream.
FileStream fs = new FileStream("DataFile.dat", FileMode.Create);
// Construct a BinaryFormatter and use it to serialize the data to the stream.
BinaryFormatter formatter = new BinaryFormatter();
try
{
formatter.Serialize(fs, addresses);
}
catch (SerializationException e)
{
Console.WriteLine("Failed to serialize. Reason: " + e.Message);
throw;
}
finally
{
fs.Close();
}
}
void LoadData()
{
// Declare the hashtable reference.
Hashtable addresses = null;
// Open the file containing the data that you want to deserialize.
FileStream fs = new FileStream("DataFile.dat", FileMode.Open);
try
{
BinaryFormatter formatter = new BinaryFormatter();
// Deserialize the hashtable from the file and
// assign the reference to the local variable.
addresses = (Hashtable) formatter.Deserialize(fs);
}
catch (SerializationException e)
{
Console.WriteLine("Failed to deserialize. Reason: " + e.Message);
throw;
}
finally
{
fs.Close();
}
// To prove that the table deserialized correctly,
// display the key/value pairs.
foreach (DictionaryEntry de in addresses)
{
Console.WriteLine("{0} lives at {1}.", de.Key, de.Value);
}
}
If your app structure is dynamic by it's nature, so it's name can be changed, location can be changed (even if , to be honest, don't understand reasons behind that) the only possibility I can see is relay in external source for retrieving or storing your config information.
In short: setup a server somewhere that holds your app configuration data, and on first startup try to reach that server, load file from it, read a data. If it fails, just load default information.
Good candidates could be : DropBox, SkyDrive, GoogleDrive, Box... find suitable C# API for any of them an store/read data you need. The only thing I would invite your attention to for this solution, is licensing. Keep an eye on it, and be sure that you can use it in your application in a way you decide to use it.
Saving the values out to a flat file...
Storing the values in an XML File, or a database file...
Windows Registry...
There are many places you can store information, and only experience will really teach you what to put where... To make an intelligent guess, you need to be familiar with all the approaches...
The only real option that isn't susceptible to the user intentionally changing the data stored on their computer, losing it due to changing machines, etc. would be to not store the data on their computer at all. Have a database or other server that you host that users connect to over the network which stores their data for them.
You may try to use the Isolated Storage
Isolated storage is not available for Windows Store apps. Instead, use
the application data classes in the Windows.Storage namespaces
included in the Windows Runtime API to store local data and files.
You may also try to use XML file to store the users setting and then store it in the SpecialFolder.ApplicationData directory.
You can also use the app.config file to save application-level settings

Small Simple Local Data Store for keeping user settings

I have this tiny C# winforms application that will NOT grow larger in any way.
It's just two input fields and a button.
I want to know if you guys have knowledge of a way to store the values that a user inputs in a local datastore. You may consider 10 records to be a lot of data for this scenario.
Requirements
It shouldn't require any setup from the database side. (table creation, etc)
I should be able to just give it an object and it should store it, I don't want to waste time on that.
The data needs to be fairly easily retrievable.
I want to be able to reuse this thing for every small app I create like this.
My Ideas
A POCO object that will be XML-Serialized and saved to the Local Settings folder. Upon loading of the app, this file is deserialized back into the POCO object.
An OODBMS: I have no experience with these but I always thought they consisted of a single dll so it would be easy to package them with the program.
I once, a long long time ago, built an application that stored user settings inside the registry. Don't know if that is still appreciated though.
What do you think is the best approach?
Code samples are very much appreciated!
I've taken both answers into account and built the following:
public static class IsolatedStorageExtensions
{
public static void SaveObject(this IsolatedStorage isoStorage, object obj, string fileName)
{
IsolatedStorageFileStream writeStream = new IsolatedStorageFileStream(fileName, FileMode.Create);
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(writeStream, obj);
writeStream.Flush();
writeStream.Close();
}
public static T LoadObject<T>(this IsolatedStorage isoStorage, string fileName)
{
IsolatedStorageFileStream readStream = new IsolatedStorageFileStream(fileName, FileMode.Open);
BinaryFormatter formatter = new BinaryFormatter();
T readData = (T)formatter.Deserialize(readStream);
readStream.Flush();
readStream.Close();
return readData;
}
}
A wrapper POCO object that contains that data to be serialized:
[Serializable]
internal class DataStoreContainer
{
public DataStoreContainer()
{
UserIDs = new List<int>();
}
public List<int> UserIDs { get; set; }
}
To consume these extensions:
private IsolatedStorageFile _isoStore = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly, null, null);
private DataStoreContainer _data = new DataStoreContainer();
private const string FILENAME = "MyAppName.dat";
And in any method where you want to get the data :
_data = _isoStore.LoadObject<DataStoreContainer>(FILENAME);
To save the data:
_isoStore.SaveObject(_data, FILENAME);
Have you looked at Isolated Storage? It stores data in a local file, specific to that user (or to the application, depending on how you specify). You can easily serialize objects to and from the store because it's stream-based. It sounds like the perfect solution for your problem.
Since you state 10 items would be a lot I would vote for #1 or a variation of #1, Binary serialized... you don't seem to indicate that being able to read the data is important and binary data should give you smaller file sizes, though if 10 is a lot this still shouldn't be important.
That being said I enjoy what I've seen of db4objects.

Categories