Save/Get ObservableCollection to/from Roaming - c#

I'm making a Universal windows store app and I have an ObservableCollection.
What I want is to save the data from the ObservableCollection somewhere so that the data can sync between desktop and phone that have the same universal app, and then I need to be able to get this data so that I can update the ObservableCollection.
I haven't done anything like this before, so I'm not sure how to proceed... Do I need to save this to an XML file, and if so, how will I sync it between the different devices. The only storage methods I know how to use are:
ApplicationData.Current.LocalSettings.Values["key"] = "something";
//Which stores "something" in local storage with key "key".
ApplicationData.Current.RoamingSettings.Values["key"] = "something";
//Which stores "something" in user's Microsoft account storage with key "key".
Last one I think looks like to what I actually want, but it wouldn't be practical to save all my ObservableCollection items and properties like this.

The easiest way is to write it in a file in the app data roaming folder; app settings aren't very convenient to store collections. You can use XML, JSON or anything else. These days the trend is to use JSON, so here's an example.
Serialization:
var folder = ApplicationData.Current.RoamingFolder;
var file = await folder.CreateFileAsync("collection.json", CreationCollisionOption.ReplaceExisting);
using (var stream = await file.OpenStreamForWriteAsync())
using (var writer = new StreamWriter(stream, Encoding.UTF8))
{
string json = JsonConvert.SerializeObject(collection);
await writer.WriteAsync(json);
}
Deserialization:
var folder = ApplicationData.Current.RoamingFolder;
var file = await folder.GetFileAsync("collection.json");
using (var stream = await file.OpenStreamForReadAsync())
using (var reader = new StreamReader(stream, Encoding.UTF8))
{
string json = await reader.ReadToEndAsync();
var collection = JsonConvert.DeserializeObject<ObservableCollection<YourClass>>(json);
}
(using JSON.NET)

Related

Read Multipart/Form-Data without touching disk

Alright, so I want to receive a Multipart/Form-Data POST request in my WebAPI, and do things with the files and form fields contained within. That's easy, like so:
var provider = new MultipartFormDataStreamProvider(Path.GetTempPath());
await Request.Content.ReadAsMultipartAsync(provider);
var formValue = provider.FormData["form_value_here"];
var files = provider.FileData.Select(d => d.LocalFileName);
It's easy to get any form field or particular file I need. However, I would like to have similar capability, but instead of saving files to disk when ReadAsMultipartAsync is called, I'd like to have them in a Stream
The reasoning for this, is that I would like to hash the values of the file(s) being posted, and reject the request if necessary, before saving the files to disk. Is there built in provider or class that I've missed that has a convenient API?
You can use MultipartMemoryStreamProvider to get the form data as a stream. You'll want something like this:
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
var provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
var fileNames = new List<string>();
foreach (var file in provider.Contents)
{
fileNames.Add(file.Headers.ContentDisposition.FileName.Trim('\"'));
var buffer = await file.ReadAsByteArrayAsync();
//hash values, reject request if needed
}
return Ok();

How can I save entire MongoDB collection to json/bson file using C#?

I have process, which first, generates lots of data which is save into mongoDB collection, then data is analyzed, and last - I want to save the whole collection to file on disk, and erase the collection.
I know I could do it easily with MongoDump.exe, but I was wondering is there any way to do it directly from c#? - I mean not running console precess with - but using some functionality that is inside MOngo C# driver.
And, if it can be done - how would I do the reverse operation in c# ? - namely: loading .bson file into collection?
Here's two methods that you can use to accomplish this:
public static async Task WriteCollectionToFile(IMongoDatabase database, string collectionName, string fileName)
{
var collection = database.GetCollection<RawBsonDocument>(collectionName);
// Make sure the file is empty before we start writing to it
File.WriteAllText(fileName, string.Empty);
using (var cursor = await collection.FindAsync(new BsonDocument()))
{
while (await cursor.MoveNextAsync())
{
var batch = cursor.Current;
foreach (var document in batch)
{
File.AppendAllLines(fileName, new[] { document.ToString() });
}
}
}
}
public static async Task LoadCollectionFromFile(IMongoDatabase database, string collectionName, string fileName)
{
using (FileStream fs = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (BufferedStream bs = new BufferedStream(fs))
using (StreamReader sr = new StreamReader(bs))
{
var collection = database.GetCollection<BsonDocument>(collectionName);
string line;
while ((line = sr.ReadLine()) != null)
{
await collection.InsertOneAsync(BsonDocument.Parse(line));
}
}
}
And here's an example of how you would use them:
// Obviously you'll need to change all these values to your environment
var connectionString = "mongodb://localhost:27017";
var database = new MongoClient(connectionString).GetDatabase("database");
var fileName = #"C:\mongo_output.txt";
var collectionName = "collection name";
// This will save all of the documents in the file you specified
WriteCollectionToFile(database, collectionName, fileName).Wait();
// This will drop all of the documents in the collection
Task.Factory.StartNew(() => database.GetCollection(collectionName).DeleteManyAsync(new BsonDocument())).Wait();
// This will restore all the documents from the file you specified
LoadCollectionFromFile(database, collectionName, fileName).Wait();
Note that this code was written using version 2.0 of the MongoDB C# driver, which you can obtain via Nuget. Also, the file reading code in the LoadCollectionFromFile method was obtained from this answer.
You can use C# BinaryFormatter to serialize object graph to disk. Also you can deserialize back to object graph.
Serialize:
https://msdn.microsoft.com/en-us/library/c5sbs8z9%28v=VS.110%29.aspx
Deserialize:
https://msdn.microsoft.com/en-us/library/b85344hz%28v=vs.110%29.aspx
However that is not mongodb or C# driver feature.
After serializing you can use the driver to drop the collection. And after deserializing you can use the driver to insert objects into a new collection.
Based on your rules, you may want to do some locking on that collection at the time you are doing the export process before you drop it.

How to get the stream for a Multipart file in webapi upload?

I need to upload a file using Stream (Azure Blobstorage), and just cannot find out how to get the stream from the object itself. See code below.
I'm new to the WebAPI and have used some examples. I'm getting the files and filedata, but it's not correct type for my methods to upload it. Therefore, I need to get or convert it into a normal Stream, which seems a bit hard at the moment :)
I know I need to use ReadAsStreamAsync().Result in some way, but it crashes in the foreach loop since I'm getting two provider.Contents (first one seems right, second one does not).
[System.Web.Http.HttpPost]
public async Task<HttpResponseMessage> Upload()
{
if (!Request.Content.IsMimeMultipartContent())
{
this.Request.CreateResponse(HttpStatusCode.UnsupportedMediaType);
}
var provider = GetMultipartProvider();
var result = await Request.Content.ReadAsMultipartAsync(provider);
// On upload, files are given a generic name like "BodyPart_26d6abe1-3ae1-416a-9429-b35f15e6e5d5"
// so this is how you can get the original file name
var originalFileName = GetDeserializedFileName(result.FileData.First());
// uploadedFileInfo object will give you some additional stuff like file length,
// creation time, directory name, a few filesystem methods etc..
var uploadedFileInfo = new FileInfo(result.FileData.First().LocalFileName);
// Remove this line as well as GetFormData method if you're not
// sending any form data with your upload request
var fileUploadObj = GetFormData<UploadDataModel>(result);
Stream filestream = null;
using (Stream stream = new MemoryStream())
{
foreach (HttpContent content in provider.Contents)
{
BinaryFormatter bFormatter = new BinaryFormatter();
bFormatter.Serialize(stream, content.ReadAsStreamAsync().Result);
stream.Position = 0;
filestream = stream;
}
}
var storage = new StorageServices();
storage.UploadBlob(filestream, originalFileName);**strong text**
private MultipartFormDataStreamProvider GetMultipartProvider()
{
var uploadFolder = "~/App_Data/Tmp/FileUploads"; // you could put this to web.config
var root = HttpContext.Current.Server.MapPath(uploadFolder);
Directory.CreateDirectory(root);
return new MultipartFormDataStreamProvider(root);
}
This is identical to a dilemma I had a few months ago (capturing the upload stream before the MultipartStreamProvider took over and auto-magically saved the stream to a file). The recommendation was to inherit that class and override the methods ... but that didn't work in my case. :( (I wanted the functionality of both the MultipartFileStreamProvider and MultipartFormDataStreamProvider rolled into one MultipartStreamProvider, without the autosave part).
This might help; here's one written by one of the Web API developers, and this from the same developer.
Hi just wanted to post my answer so if anybody encounters the same issue they can find a solution here itself.
here
MultipartMemoryStreamProvider stream = await this.Request.Content.ReadAsMultipartAsync();
foreach (var st in stream.Contents)
{
var fileBytes = await st.ReadAsByteArrayAsync();
string base64 = Convert.ToBase64String(fileBytes);
var contentHeader = st.Headers;
string filename = contentHeader.ContentDisposition.FileName.Replace("\"", "");
string filetype = contentHeader.ContentType.MediaType;
}
I used MultipartMemoryStreamProvider and got all the details like filename and filetype from the header of content.
Hope this helps someone.

Windows.Storage.FileIO - How to save objects

So I was using Isolated Storage to save my objects locally as XML. It worked, but now Microsoft has changed how it works. According to this site:
http://msdn.microsoft.com/en-us/library/3ak841sy.aspx
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.
I was doing it like this:
var _Option = Windows.Storage.CreationCollisionOption.ReplaceExisting;
var _File = await Folder.CreateFileAsync(path, _Option);
MemoryStream saveData = new MemoryStream();
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
XmlSerializer x = new XmlSerializer(data.GetType());
x.Serialize(saveData, data,ns);
if (saveData.Length > 0)
{
// Get an output stream for the SessionState file and write the state asynchronously
using (Stream fileStream = await _File.OpenStreamForWriteAsync())
{
saveData.Seek(0, SeekOrigin.Begin);
await saveData.CopyToAsync(fileStream);
await fileStream.FlushAsync();
}
return true;
}
But I don't see how to do this with the new way. Any ideas?
I recommend you to use this helper class, which stores any object in XML format in local/temp/roaming folder.
Here's blog post about that.

How do I get the name of a file generated with a stream in C#?

I'm building a Windows 8 metro app with XAML/C#. I'm saving an .xml file my data structure with a stream, like this:
XmlSerializer serializer = new XmlSerializer(typeof(MyObjectType));
using (var stream = await App.LocalStorage.OpenStreamForWriteAsync(MyObject.Title + ".xml", Windows.Storage.CreationCollisionOption.GenerateUniqueName))
serializer.Serialize(stream, MyObject);
Where:
App.LocalStorage
Is obviously a StorageFolder objecty set to
Windows.Storage.ApplicationData.Current.LocalFolder
The GenerateUniqueName option is set in order to avoid collisions, because my objects can have the same title. Now, I need to get the file name my stream generated, how can I get it?
Thank you
Try creating the file first.
var sourceFileName = MyObject.Title + ".xml";
StorageFile storageFile = await App.LocalStorage.CreateFileAsync(sourceFileName, Windows.Storage.CreationCollisionOption.GenerateUniqueName);
using (var stream = await storageFile.OpenAsync(FileAccessMode.ReadWrite))
{
serializer.Serialize(stream, MyObject);
}
The OpenStreamForWriteAsync method does not seem to give you any easy way to access this information. You could switch to accessing it another way:
StorageFile file = await App.LocalStorage.CreateFileAsync(...);
using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))
// do stuff, file name is at file.Name

Categories