So I am working on an UWP app that I'm creating in a portable class library (Xamarin). I need to save information that are typed in (e.g. in TextBoxes) by a user in an xml file.
Therefore I created a class in PCL where I get the info from those TextBoxes:
namespace myProject
{
public class XMLData
{
[XmlRoot("MyRootElement")]
public class MyRootElement
{
[XmlAttribute("MyAttribute1")] //name of the xml element
public string MyAttribute1 //name of a textboxt e.g.
{
get;
set;
}
[XmlAttribute("MyAttribute2")]
public string MyAttribute2
{
get;
set;
}
[XmlElement("MyElement1")]
public string MyElement1
{
get;
set;
}
}
}
On each Page there is a "continue" button. When clicked, the data gets saved:
async void Continue_Clicked(object sender, EventArgs e)
{
await Navigation.PushAsync(new Page2());
XMLData.MyRootElement mre = new XMLData.MyRootElement
{
MyAttribute1 = editor1.ToString(),
MyAttribute2 = editor2.ToString(),
MyElement1 = editor3.ToString()
};
}
At the last button click the file should be created and saved:
private void CreateandSave_Clicked(object sender, EventArgs e)
{
var s = DependencyService.Get<IFileHelper>().MakeFileStream();//using dependencyService to get a stream (there is no system.io.stream in PCL)
XMLData xmldat = new XMLData();
using (StreamWriter sw = new StreamWriter(s, Encoding.UTF8))
{
XmlSerializer serializer = new XmlSerializer(typeof(XMLData));
serializer.Serialize(sw, xmldat);
}
}
Here my code in the UWP class (for dependencyService I created a class called FileHelper to get a stream and create a saving location + file)
namespace myProject.UWP
{
public class FileHelper: IFileHelper //IFileHelper is a simple interface I made with the Stream MakeFileStream(); method in it
{
public async Task<IRandomAccessStream> MakeFileStreamAsync()
{
StorageFolder sf = KnownFolders.DocumentsLibrary;
var file = await sf.CreateFileAsync("data.xml", CreationCollisionOption.OpenIfExists);
using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
return stream;
}
}
Stream IFileHelper.MakeFileStream()
{
var task = MakeFileStreamAsync();
task.Wait();
return task.Result.AsStreamForWrite();
}
}
}
The problem is that whenever I reach the CreateandSave button and click on it, the app just freezes. No errors, nothing, everything looks fine. After I break up the debugging I can see that there is an xml file created in the folder I wanted but it's empty (0 bytes). What's wrong with the code? Anyone an idea?
In this piece of code:
using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
return stream;
}
you are returning an instance created by the using block. It will be disposed before the return, and as a result, you're returning a disposed object.
Change it to just return stream. The StreamWriter you use is in a using block itself, so during its disposal it will dispose the underlying stream:
The StreamWriter object calls Dispose() on the provided Stream object when StreamWriter.Dispose is called.
Your freeze problem is that the Task.Wait() instruction blocks the main UI thread until MakeFileStreamAsync() method finishes execution.
You should make this method async and returning a Task<Stream> type, and call the MakeFileStreamAsync method using the await keyword:
async Task<Stream> IFileHelper.MakeFileStream()
{
var stream = await MakeFileStreamAsync();
return stream.AsStreamForWrite();
}
Therefore, your code for the creation click should be something like:
private void CreateandSave_Clicked(object sender, EventArgs e)
{
var s = DependencyService.Get<IFileHelper>().MakeFileStream();//using dependencyService to get a stream (there is no system.io.stream in PCL)
XMLData xmldat = new XMLData();
// Here you should await your `s` Task:
await s;
using (StreamWriter sw = new StreamWriter(s, Encoding.UTF8))
{
XmlSerializer serializer = new XmlSerializer(typeof(XMLData));
serializer.Serialize(sw, xmldat);
}
}
Hope it helps!
EDIT:
Regarding your empty xml file problem, I think it's because you save the data in another page, but don't do anything with it. Which means you lose them when loading the Page2. Therefore, they are not available in the CreateandSave_Clicked method and you currently save an empty XMLData object.
The more intuitive way is passing the data to your Page2 constructor and add this datatype as a public property of Page2. So your Continue_Clicked method would look like:
async void Continue_Clicked(object sender, EventArgs e)
{
// Note you must REVERSE instructions here
// Create first your object (save the user data in it)
XMLData.MyRootElement mre = new XMLData.MyRootElement
{
MyAttribute1 = editor1.ToString(),
MyAttribute2 = editor2.ToString(),
MyElement1 = editor3.ToString()
};
// Pass it to Page2 through the constructor
await Navigation.PushAsync(new Page2(mre));
}
And so the Page2 class/contructor becomes:
public class Page2 : SomeParentClass
{
...
// add your XMLData property
public XMLData.MyRootElement mre { get; set; }
...
// the constructor
public Page2(XMLData.MyRootElement data){
// Save the user data in xmldat property. So this data could be reused later.
this.mre = data;
}
}
Then, if it's Page2 that is responsible of creating and saving the XML file itself, you can reuse the object you passed through the constructor.
private void CreateandSave_Clicked(object sender, EventArgs e)
{
var s = DependencyService.Get<IFileHelper>().MakeFileStream();//using dependencyService to get a stream (there is no system.io.stream in PCL)
// You want to remove that here as you created a public property of type XMLData.MyRootElement (called mre) holding user data instead
//XMLData xmldat = new XMLData();
// Here you should await your `s` Task:
await s;
using (StreamWriter sw = new StreamWriter(s, Encoding.UTF8))
{
// Change of serialized type here
XmlSerializer serializer = new XmlSerializer(typeof(XMLData.MyRootElement));
// Here, just seralize the property saved through constructor
serializer.Serialize(sw, mre);
}
}
Note that if it's Page3 or whatever Page that is responsible of saving the XML file, just keep passing the saved user data from page to page and save them once when you call the CreateandSave_Clicked method.
Another point is that I am not sure of the usefulness of nesting MyRootElement class into XMLData. You could just remove the XMLData nesting class and just keep the MyRootElement class as the "main" one.
You could also achieve the same using static fields as well and therefore not using the constructor. But from my point of view, it's less intuitive and moreover, less clean.
Enjoy,
Related
I have a base class like :
public class Sensor
{
public void Serialize(string path)
{
try
{
System.Xml.Serialization.XmlSerializer xml = new System.Xml.Serialization.XmlSerializer(this.GetType());
using (System.IO.StreamWriter file = new System.IO.StreamWriter(System.IO.Path.GetFullPath(path)))
{
xml.Serialize(file, this);
}
}
catch (Exception e)
{
;
}
}
public static T Deserialize<T>(string path)
{
T loaded = default(T);
try
{
System.Xml.Serialization.XmlSerializer deserializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
using (StreamReader reader = new StreamReader(path))
{
loaded = (T)deserializer.Deserialize(reader);
}
}
catch (Exception e)
{
;
}
return loaded;
}
}
Then I have a couple of classes that derive from this:
public TemperatureSensor : Sensor {}
public PositionSensor :Sensor{}
They share some common interfaces but also implement things differently.
I have a SensorController that contains a List<Sensor> with a mixture of different sensors. I want to save them to XML files and load them afterwards.
I tried a simple:
public void Load()
{
var files = Directory.GetFiles(directory, "*.xml");
foreach(var file in files)
{
var p = CodePuzzle.Deserialize<Puzzle>(file);
}
}
The problem is that when the deserializer finds the <PositionSensor> it crashes (Unexpected <PositionSensor> at 2,2).. I guess it was expecting <Sensor>
How can that be done?? Loading each Sensor in the sub-class it was originally stored in??
First you should add the tag [XmlInclude(typeof(DerivedClass))] to the base Class. So it looks like:
[Serializable]
[XmlInclude(typeof(TemperatureSensor))]
[XmlInclude(typeof(PositionSensor))]
public Class Sensor
....
Then when you save to xml file any of the derived class, save them as Sensor, not as the derived class.
var myTemp = new TemperatureSensor();
Sensor.saveToXML(myTemp,"myTemp.xml")
Then when reading the xml´s in as Sensor, you can still identify the subclass they actually belong to.
Sensor myImportedSensor = Sensor.Load("myTemp.xml")
// myImportedSensor is Sebsir returns true
// AND
// myImportedSensor is TemperatureSensor returns also true
My app is leaking about 6MB of RAM every time I navigate to or from a page and I think this is what the cause is:
I've got the following class:
public class JSONclasses
{
public class RootObject
{
public ReleaseDates release_dates { get; set; }
public int id { get; set; }
public List<Result> results { get; set; }
}
//Another few hundred variables
}
And am using it in the following way on my pages:
using static TMDBclient.JSONclasses;
namespace TMDBclient
{
public sealed partial class MainPage : AltPage
{
public string defaultbackdrop;
public string defaultposter;
public int budget;
public long revenue;
//A copy of all the variables from the JSONclasses that I need
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
string result = await SetData();
myTextBlock.Text = defaultbackdrop;
//Continue setting all the data to the UI and doing various operations on it.
}
public async Task<string> GetMovieData(string id)
{
using (HttpClient client = new HttpClient())
{
using (HttpResponseMessage response = await client.GetAsync(url))
{
using (Stream stream = response.Content.ReadAsStreamAsync().Result)
{
using (StreamReader reader = new StreamReader(stream))
{
string json = reader.ReadToEnd();
var root = JsonConvert.DeserializeObject<RootObject>(json);
defaultbackdrop = root.BackdropURL;
id = root.id;
//Continue setting all the variables in the same manner.
}
}
}
}
}
}
To recap, I'm:
Declaring JSONclasses as a static class
Setting tons of variables
Using those variables in my UI and performing various operations on them.
On another note, I've set NavigationCacheMode to Disabled, so it shouldn't be saving the entire page in memory.
Do you reckon this is what's leaking all that precious memory? I can tell this is particularly badly optimized code, and a few of you probably threw up a little inside your mouths reading it, but how can I fix it?
According to visual studio, there's definitely some And that's only by going back and forth to the same page a few dozen times.
Also, I've used ReSharper dotMemory to diagnose where the memory leak is coming from, and I'm 98% sure this is the source of the problem.
I have found some threads about this problem, like this and this but I cannot figure out how I could implement this for my code.
I have something like this:
public sealed class Party
{
public Party()
{
load();
}
....
public async void Load()
{
string fileName = this.Name + ".xml";
var files = ApplicationData.Current.LocalFolder.GetFilesAsync(Windows.Storage.Search.CommonFileQuery.OrderByName).GetResults();
var file = files.FirstOrDefault(f => f.Name == fileName);
if (file != null)
{
using (var stream = await ApplicationData.Current.LocalFolder.OpenStreamForReadAsync(fileName))
{
XmlSerializer serializer = new XmlSerializer(typeof(Party));
Party data = (Party)serializer.Deserialize(stream);
this = data;
}
}
}
}
This throws me the "cannot assign to ' this ' because it is read-only". Since I read a file and I need to await it, it have to be async, and then I cannot have the class as a return type.
Any ideas for how to deserialize this to itself?
You can't assign to this. It's an instance of an object, and it makes no sense to change it.
Either have a static method that returns the Party (and use that to create the class):
public static Party Load()
{
// ... Deserialize
return (Party)serializer.Deserialize(stream);
}
Or load the information into your class using the deserialized object (which would be inefficient, since they're the same type):
public void Load()
{
// ... Deserialize
Party data = (Party)serializer.Deserialize(stream);
this.Name = data.Name;
this.PartyInfo = data.PartyInfo;
}
Clearly, the static method should be preferred here, and is considered the factory pattern.
I have a static class wherein I am reading an XML to build a dictionary.
Now this initialization is done in the static constructor.
In order to test this Initialize method, I have to somehow stub out the reading of XML logic and just give it an XDocument for testing, but not sure how can I do that.
internal static class MasterMnemonicsLookup
{
private static Dictionary<string, StateCoverageMnemonic[]> masterMnemonics = new Dictionary<string, StateCoverageMnemonic[]>();
private static StateCoverageMnemonic[] stateCoverageMnemonics;
static MasterMnemonicsLookup()
{
Initialize();
}
private static void Initialize()
{
var resource = XDocument.Parse(GetResourceTextFile("MasterMnemonics.xml"));
var serializer = new XmlSerializer(typeof (MasterMnemonicsType));
var model = (MasterMnemonicsType) serializer.Deserialize(resource.CreateReader());
var stateCoverageMnemonicsList = new List<StateCoverageMnemonic>();
foreach (var masterMnemonic in model.MasterMnemonics)
{
var stateCoverageMnemonicsXml = new List<StateCoverageMnemonic>();
var excludedStates = RiskStates.None;
StateCoverageMnemonic allStateCoverageMnemonic = null;
foreach (var stateCoverageMnemonic in masterMnemonic.StateCoverageMnemonics)
{
var state = stateCoverageMnemonic.StateCode;
if (!state.HasFlag(RiskStates.All))
{
excludedStates = excludedStates | state;
var mnemonic = stateCoverageMnemonic.Mnemonic;
var coverageCode = stateCoverageMnemonic.CoverageCode;
var stateCoverageMnemonicTemp = new StateCoverageMnemonic(state, mnemonic, coverageCode);
stateCoverageMnemonicsXml.Add(stateCoverageMnemonicTemp);
}
else
{
//// TODO: If All occurs twice should we throw an exception
allStateCoverageMnemonic = new StateCoverageMnemonic(state, stateCoverageMnemonic.Mnemonic, stateCoverageMnemonic.CoverageCode);
}
}
if (allStateCoverageMnemonic != null)
{
stateCoverageMnemonicsXml.Add(new StateCoverageMnemonic(RiskStates.All ^ excludedStates, allStateCoverageMnemonic.Mnemonic, allStateCoverageMnemonic.CoverageCode));
}
stateCoverageMnemonicsList.AddRange(stateCoverageMnemonicsXml);
masterMnemonics.Add(masterMnemonic.MasterMnemonic, stateCoverageMnemonicsXml.ToArray());
}
stateCoverageMnemonics = stateCoverageMnemonicsList.ToArray();
}
private static string GetResourceTextFile(string filename)
{
string result = string.Empty;
using (Stream stream = typeof(MasterMnemonicsLookup).Assembly.GetManifestResourceStream("Geico.Applications.Business.CoverageApi.DomainLayer.DataLayer." + filename))
{
var streamReader = new StreamReader(stream);
result = streamReader.ReadToEnd();
}
return result;
}
}
Using static contructors in this way is not advised, and your scenario is a good example why. You might try the singleton pattern using a public instance constructor that accepts an XDocument. (You can use internal, but this makes it harder to unit test). This is a simple form of dependency injection.
For testing, an instance of your class can simply be created by your testing framework with the test XDocument.
For your live application, a static instance of your class can be initialized and held by a container type, and the appropriate XDocument can be passed in privately (within the container).
I agree with Darious Vaughan-Scott
But if you want to keep using the static constructor you may want to put the loading logic into a seperate class which makes it easier to test.
For example
internal class MasterMnemonicsLoader
{
public void Load(
XDocument resource,
Dictionary<string, StateCoverageMnemonic[]> masterMnemonics,
StateCoverageMnemonic[] stateCoverageMnemonics)
{
//Do the loading here
}
}
and in the Initialize method you can call the Load method
private static void Initialize()
{
var resource = XDocument.Parse(GetResourceTextFile("MasterMnemonics.xml"));
var loader = MasterMnemonicsLoader();
loader.Load(resource, masterMnemonics, stateCoverageMnemonics);
I am using this GetBasicPropertiesAsync method to fetch file properties one by one in a loop.
But I land up with this error:
WinRT Information : Cannot call the requested method
(GetBasicPropertiesAsync). A previous call to this method is pending
and must return before the method can be called again.
My main function has the following code to enumerate all files and folders from the pictures library.
List<BaseStorage> listOfFiles = new List<BaseStorage>();
IReadOnlyList<StorageFolder> foldersList = await curFolder.MCMFolder.GetFoldersAsync();
// For each folder found ...
foreach (StorageFolder folder in foldersList)
{
listOfFiles.Add(new Folder(folder, parents));
}
// Enumerate all files in the Pictures library.
IReadOnlyList<StorageFile> fileList = await curFolder.MCMFolder.GetFilesAsync();
// For each file found ...
foreach (StorageFile file in fileList)
{
listOfFiles.Add(new Document(file));
}
return listOfFiles;
Folder and Document class inherits BaseStorage Class.
class BaseStorage
{
public BaseStorage(IStorageItem storageItem)
{
this.Name = storageItem.Name;
this.CreationDate = storageItem.DateCreated.ToString();
setModifiedDateAndOwner(storageItem);
}
private async void setModifiedDateAndOwner(IStorageItem storageItem)
{
// await Task.Delay(500);
Windows.Foundation.IAsyncOperation<BasicProperties> basicPropsTask = storageItem.GetBasicPropertiesAsync();
BasicProperties _basicProps = await storageItem.GetBasicPropertiesAsync();
this.ModifiedDate = _basicProps.DateModified.ToString();
string fileOwnerProperty = "System.FileOwner";
List<string> propertiesToFetch = new List<string>();
propertiesToFetch.Add(fileOwnerProperty);
IDictionary<string, object> props = await _basicProps.RetrievePropertiesAsync(propertiesToFetch);
this.Owner = props[fileOwnerProperty].ToString();
return;
}
}
class Document
{
public Document()
{
setSize();
}
private async void setSize()
{
BasicProperties _basicProps = await file.GetBasicPropertiesAsync();
ulong fileSizeInBytes = _basicProps.Size;
}
}
The problem here is the method setModifiedDateAndOwner has a call to GetBasicPropertiesAsync method. Even before this method is complete, the
child class - Document calls setSize method which again has a call to GetBasicPropertiesAsync method.
This causes the exception to occur. However the behaviour is not very consistent due to threads.
How do I make sure that the method setModifiedDateAndOwner in the Base class is complete before calling the methods in its child class.
Constructors can't be async, but there are some ways to work around that.
Since you're using inheritance, you can't use the Factory Pattern from the above article directly, but you can combine the Factory Pattern for the derived class with a version of the The Asynchronous Initialization Pattern for the base class:
class BaseStorage
{
protected BaseStorage(IStorageItem storageItem)
{
this.Name = storageItem.Name;
this.CreationDate = storageItem.DateCreated.ToString();
Initialization = setModifiedDateAndOwner(storageItem);
}
protected Task Initialization { get; private set; }
private async Task setModifiedDateAndOwner(IStorageItem storageItem)
{
…
}
}
class Document : BaseStorage
{
private Document(IStorageFile storageFile)
: base(storageFile)
{ }
public static async Task<Document> Create(IStorageFile storageFile)
{
var result = new Document(storageFile);
await result.Initialization;
return result;
}
}
This way, you have to be careful when implementing Document and other classes that inherit from BaseStorage, but you can only use those classes correctly (you have to await the Task to get the created instance).