I'm at my first Windows Store App, and I'm having some issue with Deserialization/loading my application Data.
I'm using JSon to serialize my class object to a string, then I'm saving it to the Application Local App folder.
When it is time to load the data, once upon a time the load fail at run time, and no data are loaded. and my classes are instantiated as a new classes.
But when I'm running my code step my step in debug mode. the Desirialization work OK and my classes are correctly loaded.
I'm thinking that the problem come from my async implementation. but I can't tell where.
Can some one help me?
Save procedure:
public virtual void Save()
{
var data = JsonConvert.SerializeObject(this);
WriteTotextFileAsync("Cashier.Data.Config", data);
}
public static async void WriteTotextFileAsync(string fileName, string contents)
{
var folder = ApplicationData.Current.LocalFolder;
var file = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
await FileIO.WriteTextAsync(file, contents);
}
Load procedure:
private async void LoadConfig()
{
string data;
if (Config == null)
{
data = await ReadTextFileAsync("Cashier.Data.Config");//.ConfigureAwait(false);
if (data != null && data.Length > 0)
try
{
System.Diagnostics.Debug.WriteLine(data);
Config = JsonConvert.DeserializeObject<Data.Config>(data);
System.Diagnostics.Debug.WriteLine(data);
}
catch (Exception ex)
{
var dlg = new MessageDialog("Desirialization erreur" + Environment.NewLine + ex.Message);
dlg.ShowAsync();
Config = new Config();
}
else
{
System.Diagnostics.Debug.WriteLine("Config = new Config();");
Config = new Config();
}
}
}
public static async Task<string> ReadTextFileAsync(string path)
{
var folder = ApplicationData.Current.LocalFolder;
try
{
var file = await folder.GetFileAsync(path);
var value = await FileIO.ReadTextAsync(file);
return value;
}
catch (System.IO.FileNotFoundException ex)
{
return string.Empty;
}
}
You should avoid async void. Change your LoadConfig and Save methods into async Task methods and have your calling code await the task they return.
One problem with async void is that the calling code cannot know when it has completed. E.g., the code that calls LoadConfig does not know when the config is actually loaded. By making LoadConfig return a Task, then the calling code can await that Task and know that the config is loaded when that task completes.
I finally fond what went wrong. When I was deserializing the string, I was casting it to the wrong object class type.
Related
I'm completely new to Unity development and I'm trying to integrate an asynchronous functionality into an existing Coroutine but I have faced several issues so far.
My issues:
The Unity app completely freezes, probably because it was blocked by a thread. I implemented this code in traditional C# (console app) without having any issues.(Fixed now in Unity after some modifications)
The download task begins but it never finishes . This happens only when I run it as APK. On Unity debugging on PC works fine.
My code:
public void Validate()
{
StartCoroutine(DoWork());
}
private IEnumerator DoWork()
{
bool success;
//Perform some calculations here
..
..
success = true;
//
yield return new WaitForSeconds(3);
if(success){
GetInfo(_files);
}
}
async void GetInfo(List<string> files)
{
await StartDownload(files);
//After completing the download operation, perform some other actions in the background
...
...
//
//When done, change the active status of specific game objects
}
public async Task StartDownload(List<string> files){
var t1 = GetFileSizesAsync(files);
var t2 = DownloadFilesAsync(files);
await Task.WhenAll(t1, t2);
}
public async Task GetFileSizesAsync(List<string> urls)
{
foreach (var url in urls)
GetFileSize(url);
txtSize.text = totalSizeMb +"MB";
}
private void GetFileSize(string url)
{
var uri = new Uri(url);
var webRequest = HttpWebRequest.Create(uri);
webRequest.Method = "GET";
try
{
var webResponse = webRequest.GetResponse();
var fileSize = webResponse.Headers.Get("Content-Length");
var fileSizeInMegaByte = Math.Round(Convert.ToDouble(fileSize) / 1024.0 / 1024.0, 2);
totalSizeMb = totalSizeMb + fileSizeInMegaByte;
}
catch (Exception ex)
{
}
finally
{
webRequest.Abort();
}
}
public async Task<List<string>> DownloadFilesAsync(List<string> urls)
{
var result = new List<string>();
foreach (var url in urls)
var download = await DownloadFile(url);
if(download)
result.Add(url);
return response;
}
private async Task<bool> DownloadFile(string url)
{
WebClient webClient = new WebClient();
var uri = new Uri(url);
var saveHere = "C:\\...";
try
{
await webClient.DownloadFileTaskAsync(uri, saveHere);
return true;
}
catch (Exception ex)
{
return false;
}
}
Can you tell me what I'm doing wrong here? I've tried several ways but couldn't manage to find a proper solution.
Thank you!
I would rather make it
async Task GetInfo(List<string> files){ ... }
And then in your Coroutine do
var t = Task.Run(async () => await GetInfo(files));
while(!t.IsCompleted)
{
yield return null;
}
// Or also
//yield return new WaitUntil(() => t.IsCompleted);
if(!t.IsCompletedSuccesfully)
{
Debug.LogError("Task failed or canceled!");
yield break;
}
Note however:
When done, change the active status of specific game objects
This can't be done async! It has to happen in the Unity main thread! Therefore you would probably rather return something from your GetInfo task and activate the objects in the Coroutine when done.
after the loop and yielding you could then access the return value via
var result = t.Result;
Your web requests are currently totally redundant! You start get requests only to check how big the received content is but immediately throw away that received content ... then you start a second request to actually download the files (again).
In general I would recommend to rather use a UnityWebRequest.Get you can directly yield in the Coroutine in combination with a DownloadHandlerFile which allows you to directly download the content into a local file instead of into runtime memory.
Also
var saveHere = "C:\\...";
is hopefully not what you are trying to use as path on Android ;)
First of all.
The freezing is definitely being done by the IEneumerator as I don't see a yield statement. What's a yield statement? Uhhhh...Google it. lel
Second of all.
Wouldn't a normal void work just fine?
Third of all.
I don't know much about async's since I'm pretty new to them
but I'm fairly certain you don't need them for:
Task GetFileSizesAsync(List urls)
AND
async Task<List> DownloadFilesAsync(List urls)
I may be wrong tho.
I am trying to open a .txt file for my UPW app.
string text;
private async void OpenFile(string fileName)
{
StorageFolder localFolder = await ApplicationData.Current.LocalFolder.GetFolderAsync("Beginner");
try
{
StorageFile file = await localFolder.GetFileAsync(fileName + ".txt");
text = await FileIO.ReadTextAsync(file);
PrintMessage(text);
}
catch (Exception)
{
PrintMessage("Failed to load file");
}
}
public async void PrintMessage(string message)
{
//Writes message
MessageDialog msg = new MessageDialog(message);
await msg.ShowAsync();
}
public Main()
{
OpenFile("WordList");
PrintMessage(text);
}
The code runs fine when I have PrintMessage(text); after ReadTextAsync. When I delete it, the program will freeze most of the time. When I run it in debugger, it mostly gets done, but sometimes it freezes at line StorageFile file = await localFolder.GetFileAsync(fileName + ".txt");. I believe there is some problem with async magic.
Also, the text variable in Main() is almost always null even though it should contain text of the file.
All I need is a function that will reliably open a .txt file and return (either return return, or like this into global) content of that file. I tried using Task<> but I couldnt make it work either...
Edit:
I tried
string text { get; set; }
public async void OpenFileAssist(string fileName)
{
await OpenFile(fileName);
}
public async Task OpenFile(string fileName)
{
StorageFolder localFolder = await Windows.Storage.ApplicationData.Current.LocalFolder.GetFolderAsync("Beginner");
try
{
StorageFile file = await localFolder.GetFileAsync(fileName + ".txt");
text = await FileIO.ReadTextAsync(file);
}
catch (Exception)
{
PrintMessage("File does not exist");
}
}
But it still does not work... When I call OpenFileAssist() from Main() the text variable is still null. :( If I open it in debugger and go step by step it works, though.
I got solution by using
private async void Page_Loaded(object sender, RoutedEventArgs e)
See this page, especially the example using TaskScheduler.FromCurrentSynchronizationContext(). It helped me a lot.
Please forgive me for any noobish mistakes seen below, I'm learning some of the concepts I'm attempting to work with.
Problem:
While debugging my app, I was able to call an async function with Task.Start(). I felt that the app was in a working state for the phase I'm in so removed all breakpoints with CTRL + SHIFT + F9.
Once I ran the app with no breakpoints it would fail due to a property not getting populated. Now when I try to debug any breakpoint I set in the async function that handles most of the work is longer hit. It's like it is getting skipped. Can anyone see a reason why GetWowAuctionFileInfo isn't being called?
GetWowAuctionFileInfo is what is not getting called, or at least appears to be not getting called.
Thanks.
Relevant Code
Caller Function
private void buttonTestJSFCHI_Click(object sender, RoutedEventArgs e)
{
JSON_Worker w = new JSON_Worker();
w.StartTask("FileInfo", "https://us.api.battle.net/wow/auction/data/medivh?locale=en_US&apikey=<guid>");
foreach (string res in w.ReturnedData)
{
textBoxResults.Text += res;
}
}
Called Functions
public void StartTask(string TaskName, string optionalUri= "no_uri_passed")
{
if (TaskName == "FileInfo")
{
//Need to use a lamba expression to call a delegate with a parameter
if (!(optionalUri == "no_uri_passed"))
{
Task t = new Task(() => GetWowAuctionFileInfo(optionalUri));
t.Start();
//Func<string> function = new Func<string>(() => GetWowAuctionFileInfo(optionalUri));
//Task<string> tInfo = Task<string>.Factory.StartNew(() => GetWowAuctionFileInfo(optionalUri));
}
}
}
private async void GetWowAuctionFileInfo(string auctionInfoUri)
{
RealmJSFileCheck realmInfoObject;
List<string> returnValue = new List<string>();
try
{
using (HttpClient client = new HttpClient())
{
for (int attempt = 0; attempt < 3; attempt++)
{
var response = await client.GetAsync(auctionInfoUri);
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
realmInfoObject = JsonConvert.DeserializeObject<RealmJSFileCheck>(content);
returnValue = ConvertFileInfoToConsumableList(realmInfoObject);
//returnValue = realmInfoObject.files.ToString();
break;
}
}
}
}
catch (InvalidOperationException iOpEx)
{
//recieved this when an invalid uri was passed in
}
ReturnedData = returnValue;
}
private List<string> ConvertFileInfoToConsumableList(RealmJSFileCheck jsfc)
{
List<string> returnData = new List<string>();
if (jsfc.files.Count > 0)
{
StringBuilder sb = new StringBuilder();
sb.Append("File URL: ");
sb.Append(jsfc.files[0].url);
returnData.Add(sb.ToString());
sb = new StringBuilder();
sb.AppendLine("Last Modified: ");
sb.Append(jsfc.files[0].lastModified);
returnData.Add(sb.ToString());
}
else
{
returnData.Add("No File Info Found");
}
return returnData;
}
UPDATE
Thanks again all for the detailed commentary. I've gone through much documentation regarding Task usage and learned a lot in this exercise. I'm marking the answer from #Johnathon as the solution because it provided exactly what I was asking for and provided a very helpful link for more information.
Your GetWowAuctionFileInfo method is an asynchronous method, and you await an async call within it without returning a Task. In general it is bad practice to use async void. Instead, turn your GetWowAuctionFileInfo method into async Task<List<string>> GetWowAuctionFileInfo. This will let you await the GetAsync call, parse the data, and return the collection to the caller without having to use a ReturnObject.
private async Task<List<string>> GetWowAuctionFileInfo(string auctionInfoUri)
{
RealmJSFileCheck realmInfoObject;
List<string> returnValue = new List<string>();
try
{
using (HttpClient client = new HttpClient())
{
for (int attempt = 0; attempt < 3; attempt++)
{
var response = await client.GetAsync(auctionInfoUri);
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
realmInfoObject = JsonConvert.DeserializeObject<RealmJSFileCheck>(content);
// You can just return the List<T> now.
return ConvertFileInfoToConsumableList(realmInfoObject);
//returnValue = realmInfoObject.files.ToString();
break;
}
}
}
}
catch (InvalidOperationException iOpEx)
{
//recieved this when an invalid uri was passed in
}
}
Because the method was originally async void, you could not await the calling of it in your buttonTestJSFCHI_Click. Now that we've made it all Task based, you can await it within your event handler. Note that event handlers are generally the only acceptable place to use async void. Any time you are responsible for the creation of the methods, and not constrained by a contract (like event handlers), you should always return a Task on your async methods.
private async void buttonTestJSFCHI_Click(object sender, RoutedEventArgs e)
{
JSON_Worker w = new JSON_Worker();
List<string> results = await w.StartTask("FileInfo", "https://us.api.battle.net/wow/auction/data/medivh?locale=en_US&apikey=<guid>");
foreach (string res in results)
{
textBoxResults.Text += res;
}
}
public async Task<List<string>> StartTask(string TaskName, string optionalUri= "no_uri_passed")
{
if (TaskName == "FileInfo")
{
//Need to use a lamba expression to call a delegate with a parameter
if (!(optionalUri == "no_uri_passed"))
{
// Since the GetWowAuctionFileInfo now returns Task, we don't need to create a new one. Just await the Task given back to us, and return the given result.
return await GetWowAuctionFileInfo(optionalUri);
}
}
}
The reason you saw the expected result while debugging is because the debug session was slow enough that the async operation completed in time for your code to use it. When running the app outside of the debugger, it runs faster than the async operation could complete, preventing you from seeing the data. Thus the need to await the entire async call stack, so you can prevent further execution from happening down that code-path until you receive all of the desired data.
Microsoft has a good write up on Task based programming, I'd take a read through it to help you understand it some.
EDIT
Just to clarify, when you return a Task<T> on your methods, you will be given the result when you await. For example:
List<string> result = await StartTask();
Even though StartTask returns Task<List<string>>, the await operation will wait for the StartTask() method to complete, and then unwrap the result from the Task<T> object and give you the result back automatically. So don't let the method signature fool you, if you await it, you will be given back the resulting data, and not the actual Task itself. There won't be any need for you to pull the data out of the Task manually.
Because you not waiting for result.
You loop with ReturnedData before it was assigned with data.
I think you don't need to create new Task at all. Make GetWowAuctionFileInfo method properly asynchronous which returns Task.
private async Task GetWowAuctionFileInfo(string auctionInfoUri)
{
// same code
}
Change StartTask to return Task. Because we not awaiting result here we don't need make method asynchronous.
Suggest to change name of this method to LoadData for example, which give more information about what this method does.
public Task StartTask(string TaskName, string optionalUri= "no_uri_passed")
{
if (TaskName == "FileInfo")
{
//Need to use a lamba expression to call a delegate with a parameter
if (!(optionalUri == "no_uri_passed"))
{
return GetWowAuctionFileInfo(optionalUri) // this will return Task
}
}
// if validation fails - return completed task or throw exception
return Task.CompletedTask;
}
Then you can call it in Button_Click event handler
private async void buttonTestJSFCHI_Click(object sender, RoutedEventArgs e)
{
JSON_Worker w = new JSON_Worker();
await w.StartTask("FileInfo", "yourUrl");
// This line will be executed only after asynchronous methods completes succesfully
// or exception will be thrown
foreach (string res in w.ReturnedData)
{
textBoxResults.Text += res;
}
}
im trying to Reading/Writing Async Files for an Universal App in c#.
When i write and read a file for first time, it works... But when i retry it immeadiatly, there are two Errors: 1. UnauthorizedAccess 2. Handle with the OPLOCK has been closed
It seems that the methods arent finished yet and so the data is not free
(in my frame is a button which adds a new member to a List, then the list shall serialized in an XML data. When i reNavigate to that page, that XML sheet shall be deserialized back to that List, because the Content shall be displayed)
List<Immobilie> immoListe = new List<Immobilie>();
private const string FileName_ImmoObjects = "ImmoObjects.xml";
StorageFolder sFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
IStorageFile latestImmoListFile;
public Startmenue()
{
this.InitializeComponent();
immoListe.Add(new Immobilie()); // for testing creating an XML first
immoListe[0].adresse = "Foo1";
immoListe.Add(new Immobilie());
immoListe[1].adresse = "Foo2";
WriteImmoListAsync();
ReadImmoListAsync(); // These two steps working
WriteImmoListAsync(); // everything more causes error
ReadImmoListAsync();
}
public async void WriteImmoListAsync()
{
try
{
IStorageFolder folder = await sFolder.CreateFolderAsync("Saves", CreationCollisionOption.OpenIfExists);
latestImmoListFile = await folder.CreateFileAsync(FileName_ImmoObjects, CreationCollisionOption.ReplaceExisting);
using (IRandomAccessStream stream = await latestImmoListFile.OpenAsync(FileAccessMode.ReadWrite))
using (Stream outputStream = stream.AsStreamForWrite())
{
DataContractSerializer serializer = new DataContractSerializer(typeof(List<Immobilie>));
serializer.WriteObject(outputStream, immoListe);
}
}
catch (Exception e)
{
var d = new MessageDialog(e.ToString());
await d.ShowAsync();
}
}
public async void ReadImmoListAsync()
{
int i = 0;
try
{
IStorageFolder folder = await sFolder.GetFolderAsync("Saves");
i = 1;
latestImmoListFile = await folder.GetFileAsync(FileName_ImmoObjects);
i = 2;
using (IRandomAccessStream stream = await latestImmoListFile.OpenAsync(FileAccessMode.Read))
{
i = 3;
using (Stream inputStream = stream.AsStreamForRead())
{
i = 4;
DataContractSerializer deserializer = new DataContractSerializer(typeof(List<Immobilie>));
i = 5;
immoListe = (List<Immobilie>)deserializer.ReadObject(inputStream);
}
}
}
catch (Exception e)
{
var d = new MessageDialog("Fehler I = " + i + "\n" + e.ToString());
await d.ShowAsync();
}
}
So what can i do and why is it so difficult??(normal I/O is easy-peasy).-.
As I describe in my MSDN article on async best practices, you should avoid async void:
public async Task WriteImmoListAsync();
public async Task ReadImmoListAsync();
Once your methods are properly async Task, then you can await them:
await WriteImmoListAsync();
await ReadImmoListAsync();
await WriteImmoListAsync();
await ReadImmoListAsync();
You can't start the methods again until you wait for them to complete. What that above code is trying to do is to write to a file, but while that's processing, it tries to open the file and write to it while the first method call hasn't completed. You need to wait for those method calls to finish before running them again - using the await keyword would be helpful here
It might be that the process writing/reading the file are still attached to the file. You might want to take a look at this pattern for async file read/write from Microsoft:
https://msdn.microsoft.com/en-ca/library/mt674879.aspx
Also, note that if the read and write are done from differents process, you're going to have to use a mutex. Here's a great explanation on how it works:
What is a good pattern for using a Global Mutex in C#?
i am trying to download images from server and save it in local file.
i tried
private async void save()
{
Uri source = new Uri("http://www.google.ca/intl/en_com/images/srpr/logo1w.png");
StorageFile destinationFile;
try
{
destinationFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(
"downloadimage.jpg", CreationCollisionOption.GenerateUniqueName);
}
catch (FileNotFoundException ex)
{
System.Diagnostics.Debug.WriteLine("bingooooo"+ex.ToString());
return;
}
BackgroundDownloader downloader = new BackgroundDownloader();
DownloadOperation download = downloader.CreateDownload(source, destinationFile);
await download.StartAsync();
ResponseInformation response = download.GetResponseInformation();
}
but it doesn't work any alternative
Your code is fine, it's just lacking Task as return type. You should use return type as Task rather than void if your method is asynchronous. If return type is string & method is asynchronous then return type should be Task<string>.
Change private async void save() to private async Task save().
When you call save() append await keyword.
Check out Diving deep with WinRT and await