While on my local box the following code works:
public async Task<GameStatistic> LoadReport()
{
var folder = await ApplicationData.Current.LocalFolder.CreateFolderAsync(FolderName, CreationCollisionOption.OpenIfExists);
var file = await folder.GetFileAsync(FileName);
GameStatistic returnValue;
using (var inStream = await file.OpenSequentialReadAsync())
{
var serializer = new DataContractJsonSerializer(typeof (GameStatistic));
returnValue = serializer.ReadObject(inStream.AsStreamForRead()) as GameStatistic;
}
return returnValue;
}
Code that calls the above method:
public GameStatistic GetReportData()
{
var repo = new GameRepository();
var gameStatTask = repo.LoadReport(); //(awaitable) Task<GameStatistic>
gameStatTask.Wait(); //this seems to make no difference
return gameStatTask.Result;
}
But When I move to code to my Surface Pro and run the application (no debugger), the folder.GetFileAsync(FileName) fails because the async call to get the folder hasn't returned yet.
When I debug the application on my Surface Pro (via Remote Machine) and slowly walk the debugger past the first line of code and wait a few seconds, and then step again, everything works.
I don't like the idea of trying to put a thread to sleep for an arbitrary length of time, but I am not sure what else I can do here.
Is there something I am doing wrong or something I should be doing that I am not doing at all?
Is there a common practice that would really wait until the CreateFolderAsync returns so that when I call folder.GetFileAsync that I could be sure the preceding line was complete?
Thanks for any help you may be able to provide.
As #J.B points out you need to use await instead of wait. Also, any function that calls an async method should itself be async (there is at least one exception to this). So almost your entire call stack up to the UI must be changed to be some variation of async Task<...>...:
async public Task<GameStatistic> GetReportData()
{
var repo = new GameRepository();
return await repo.LoadReport(); //(awaitable) Task<GameStatistic>
}
Caller of above (just an arbitrary method):
async public Task<MyResultClass> GetReportAndResult()
{
var gameStat = await GetReportData();
return ReportDataToMyResult(gameStat);
}
Top of call chain (event handler) must be async void:
async void GetReportData_ButtonClick(...)
{
var result = await GetReportAndResult();
// do something with result
// ...
}
Related
(Edited to add more detail about every call I'm making)
I have a Xamarin Forms application connecting to a .Net Core 2.2 web service hosted in Azure App Services.
In my view model I have a call like this:
private async Task GetItems() {
var result = await itemsListFactory.GetItemsAsync()
}
Which calls this:
public async Task<IEnumerable<IItemInfo>> GetItemsAsync() {
return await ItemList.GetItemListAsync();
}
Which calls this (CSLA business object):
public static async Task<ItemList> GetItemListAsync() {
return await DataPortal.FetchAsync<ItemList>();
}
Which calls this:
[Fetch]
private async void DataPortal_Fetch() {
var rlce = RaiseListChangedEvents;
RaiseListChangedEvents = false;
IsReadOnly = false;
using (var ctx = Dal.DalFactory.GetManager()) {
var dal = ctx.GetProvider<IItemDal>();
List<ItemDto> list = null;
list = await dal.FetchAsync();
foreach (var item in list) {
Add(DataPortal.FetchChild<ItemInfo>(item));
}
}
IsReadOnly = true;
RaiseListChangedEvents = rlce;
}
Which calls:
public async Task<List<ItemDto>> FetchAsync() {
var resultSet = new List<ItemDto>();
var connectionManager = ServiceLocator.Current.GetInstance<IAzureConnectionManager>();
using (var conn = await connectionManager.GetOpenConnectionAsync()) {
/* Reading from DB */
}
return resultSet;
}
The implementation of the AzureConnectionManager looks like this:
public async Task<SqlConnection> GetOpenConnectionAsync()
{
var accessToken = await new AzureServiceTokenProvider().GetAccessTokenAsync("https://database.windows.net/");
var connection = new SqlConnection(dbconnection) {
AccessToken = accessToken
};
await connection.OpenAsync();
return connection;
}
However, the first time I make this call (e.g. first call of the day, or after not using the service for a while) I get no results back. Any subsequent calls seem to work just fine. My guess is this has something to do with the service having to take a few "extra steps" to return data due to inactivity.
This suspicion seems to be confirmed whenever I debug the web service and set breakpoints in my view model as well as the server-side code. Whenever the service's call returns with no records it's almost as if it's returning early from the server, because it returns to the view model with no data, and then my debugger hops back onto the server after it's received the access token. So, it's as if my code decided not to wait for the GetAccessTokenAsync and OpenAsync to finish what they had to do before returning to the client.
I can fix this by adding a .Result to GetAccessTokenAsync() and .Wait() to OpenAsync() like this:
public async Task<SqlConnection> GetOpenConnectionAsync()
{
var accessToken = new AzureServiceTokenProvider().GetAccessTokenAsync("https://database.windows.net/").Result;
var connection = new SqlConnection(dbconnection) {
AccessToken = accessToken
};
connection.OpenAsync().Wait();
return connection;
}
But this feels like a hack.
I doubt this is the way I'm supposed to fix this, but maybe it is. At the very least I'd like to just understand what's going on here if this is the correct way to handle this situation.
The await operator suspends evaluation of the enclosing async method until the asynchronous operation represented by its operand completes. When the asynchronous operation completes, the await operator returns the result of the operation, if any. When the await operator is applied to the operand that represents already completed operation, it returns the result of the operation immediately without suspension of the enclosing method. The await operator doesn't block the thread that evaluates the async method. When the await operator suspends the enclosing async method, the control returns to the caller of the method.
Official Document on this
So if we look at the what the documents say about Async/Await you'll notice that
When the await operator is applied to the operand that represents already completed operation, it returns the result of the operation immediately without suspension of the enclosing method.
More then likely OpenAsync(); Is seen as a operand that's already completed as you might not be awaiting your Returns, So the Operation Runs retrieve's your data but because your not suspending anything in OpenAsync It might assume the operand is already completed on the first instance and just continue then the data is loaded so on your second attempt you have the data to work with, as its already populated on the first try.
So i'd like to see a bit more code actually.
However one thing I will say is that .Wait() is Bad If you have to wait for a result and force that wait the better way to do this is .GetAwaiter().GetResult() I can link you a Seminar that explains in details about this. But in Essence .Wait() Throws exceptions into the void and make them extremly difficult to track(Or at-least far more difficult then you'd want them to be)
"Also note in no way am I anywhere near a Expert in Async/Await so feel free to correct me"
My async method is as below:
public async Task<List<object>> handleSummaryOfWallets()
{
string token = giveMeToken("URL AND CREDS");
Channel channel = new Channel("NANANANA GIROUD", ChannelCredentials.Insecure);
OMGadminAPI.OMGadminAPIClient client = new OMGadminAPI.OMGadminAPIClient(channel);
var summaryBalancesParams = new OMGadminAPIGetCurrenciesSummariesParams();
summaryBalancesParams.AdminAuthTokenSecret = token;
List<object> summariesCurrenciesOMGadmin = new List<object>();
using (var call = client.GetCurrenciesSummaries(summaryBalancesParams))
{
while (await call.ResponseStream.MoveNext())
{
OMGadminAPICurrencySummary currencySummary = call.ResponseStream.Current;
summariesCurrenciesOMGadmin.Add(currencySummary);
Console.WriteLine(summariesCurrenciesOMGadmin);
}
return summariesCurrenciesOMGadmin;
}
}
As you can see, above async method returns list of objects. I call this method as below:
var listOfBalances = balances.handleSummaryOfWallets().Wait();
and it gives me error:
Error CS0815: Cannot assign void to an implicitly-typed variable
From the error, I understand that this is not correct way to call async method. But I need to read ready list of objects from async fetched data. Its request-response, no real stable stream. So I need to generate this list only once per request. I'm using gRPC framework for RPC calls.
Please help me fetch this data and make ready to use.
The Task.Wait method waits for the Task to complete execution. It returns void. That is the reason why the exception.
Now to overcome the exception and to read the return value, one way is as mentioned in other answer and the comments; await the call as below:
public async void TestAsync()
{
var listOfBalances = await handleSummaryOfWallets();
}
Note that your calling method should also be async method now.
As you are calling Wait in your code, it looks that you want the result immediately; you have nothing else left to do that does not depend on result. In that case, you may choose to stop async chain by calling Wait. But you need to do some changes as below:
public void TestAsync()
{
var task = handleSummaryOfWallets();//Just call the method which will return the Task<List<object>>.
task.Wait();//Call Wait on the task. This will hold the execution until complete execution is done.
var listOfBalances = task.Result;//Task is executed completely. Read the result.
}
Note that calling method is no longer async. Other explanation is given in code-comments.
Other short alternative to above code is as below:
public void TestAsync()
{
var listOfBalances = handleSummaryOfWallets().Result;
}
Just use await while calling your method
var listOfBalances = await balances.handleSummaryOfWallets();
I need to read a numeric value (a version number) from a text file in my resources.
I compare this version number to the version number of an installed component.
If the version number in the resources is higher than the installed version, I copy the new component (a database) from my resources to a local directory where the user can use it.
I need to do this synchronously because my application can't work without the database.
However, I don't see any way to do it synchronously.
MS forces me to do it with an async task like this:
private async Task<string> ResourcesReadTextFile(string uFileName)
{
string sRet = "";
try
{
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri(cstAssets + uFileName));
using (var inputStream = await file.OpenReadAsync())
using (var classicStream = inputStream.AsStreamForRead())
using (var streamReader = new StreamReader(classicStream))
{
while (streamReader.Peek() >= 0)
{
sRet = streamReader.ReadLine();
}
}
}
catch (Exception ex)
{
Debug.Assert(false);//check here
}
return sRet;
}
Now I've encountered a situation where the app started before the database was copied over to the local directory as the copying also needs to be done asynchronously, there simply isn't any way to do it synchronous.
There is no such function as StorageFile.Copy().
What I'm therefore using is:
private async void pCopyFromResourcesToLocal(string uFileName)
{
// Cant await inside catch, but this works anyway
try
{
StorageFile storfile = await StorageFile.GetFileFromApplicationUriAsync(new Uri(cstAssets + uFileName));
await storfile.CopyAsync(ApplicationData.Current.LocalFolder);
}
catch (Exception ex)
{
Debug.WriteLine("");
}
}
This drives me crazy.
People write that Async should be embraced and hugged and appreciated, but in my case it causes nothing but trouble.
I don't see any way of making this thing synchronous, and I wonder why MS forces me to do it that way.
Any help very much appreciated.
Thank you.
Code as a screenshot:
Edit: I've added the top methods here:
public static async Task<DB> InitAppDb()
{
IFileHelper helper = DependencyService.Get<IFileHelper>();
string path = await helper.GetFilePathAndCopyFromResourcesIfNotPresent("tablet.db");
return (_dbApp = new DB(path));
}
public async Task CopyDatabaseIfNotExists(string uFileName)
{
IsolatedStorageFile nExpectedFolder = IsolatedStorageFile.GetUserStoreForApplication();
bool bCopyNewDB = false;
Task<bool> datatask = pResourceIsNewer(uFileName);
bCopyNewDB = await datatask;
if (! bCopyNewDB)
{
try
{
await ApplicationData.Current.LocalFolder.GetFileAsync(uFileName); //nExpectedFolder.GetFileAsync(dbPath);/// ApplicationData.Current.LocalFolder.GetFileAsync("preinstalledDB.db");
// No exception means it exists
return;
}
catch (System.IO.FileNotFoundException)
{
// The file obviously doesn't exist
}
}
pCopyFromResourcesToLocal(uFileName);
}
private async Task<bool>pResourceIsNewer(string uPath)
{
string sFileNameAppDBVersion =uPath + ".txt";
if (IsolatedStorageFileExist(sFileNameAppDBVersion))
{
int iAppDBVersionInstalled = Convert.ToInt32(IsolatedStorageReadTextFile(sFileNameAppDBVersion));
Task<string> datatask = ResourcesReadTextFile(sFileNameAppDBVersion);
string s = await datatask;
int iAppDBResources = Convert.ToInt32(s);
bool b = (iAppDBResources > iAppDBVersionInstalled);
return b;
}
else
{
return true;
}
}
All you have to do is:
//private async void pCopyFromResourcesToLocal(string uFileName) { ... }
private async Task pCopyFromResourcesToLocal(string uFileName) { ... }
and then you can await it:
//pCopyFromResourcesToLocal(uFileName);
await pCopyFromResourcesToLocal(uFileName);
and it will all be completed before you call return (_dbApp = new DB(path));
Nothing in this async/await chain can happen out of order.
When you say that your app can't work without the database, remember that using an await keyword does just that, so the following line of code will not execute until after the async call returns. You can structure your code such that it is responsive while you wait for the DB to come back online.
However, you can force your function to be synchronous by using a construct such as:
StorageFile.GetFileFromApplicationUriAsync(new Uri(cstAssets + uFileName)).GetAwaiter().GetResult();
Or, even better, have a look at the JoinableTaskFactory.
Any asynchronous API method can be made synchronous by simply tagging .GetAwaiter().GetResult() on the end, as #pm_2 says.
In the case of Task<T> results, you can simply use .Result, and for Task results, .Wait().
You can either write an asynchronous function to read your file, then wait for its result at the top level, or you can write a synchronous method and wait for the result of every call it makes to async functions.
So Microsoft is not forcing you into anything: it's just providing a simpler API with the lowest common denominator of both asynchronous and synchronous workflows.
I've been reading examples for a long time now, but unfortunately I've been unable to apply the solutions to the code I'm working with. Some quick Facts/Assorted Info:
1) I'm new to C#
2) The code posted below is modified from Amazon Web Services (mostly stock)
3) Purpose of code is to compare server info to offline already downloaded info and create a list of need to download files. This snip is for the list made from the server side, only option with AWS is to call async, but I need this to finish before moving forward.
public void InitiateSearch()
{
UnityInitializer.AttachToGameObject(this.gameObject);
//these are the access key and secret access key for credentials
BasicAWSCredentials credentials = new BasicAWSCredentials("secret key", "very secret key");
AmazonS3Config S3Config = new AmazonS3Config()
{
ServiceURL = ("url"),
RegionEndpoint = RegionEndpoint.blahblah
};
//Setting the client to be used in the call below
AmazonS3Client Client = new AmazonS3Client(credentials, S3Config);
var request = new ListObjectsRequest()
{
BucketName = "thebucket"
};
Client.ListObjectsAsync(request, (responseObject) =>
{
if (responseObject.Exception == null)
{
responseObject.Response.S3Objects.ForEach((o) =>
{
int StartCut = o.Key.IndexOf(SearchType) - 11;
if (SearchType == o.Key.Substring(o.Key.IndexOf(SearchType), SearchType.Length))
{
if (ZipCode == o.Key.Substring(StartCut + 12 + SearchType.Length, 5))
{
AWSFileList.Add(o.Key + ", " + o.LastModified);
}
}
}
);
}
else
{
Debug.Log(responseObject.Exception);
}
});
}
I have no idea how to apply await to the Client.ListObjectsAsync line, I'm hoping you all can give me some guidance and let me keep my hair for a few more years.
You can either mark your method async and await it, or you can call .Wait() or .Result() on the Task you're given back.
I have no idea how to apply await to the Client.ListObjectsAsync line
You probably just put await in front of it:
await Client.ListObjectsAsync(request, (responseObject) => ...
As soon as you do this, Visual Studio will give you an error. Take a good look at the error message, because it tells you exactly what to do next (mark InitiateSearch with async and change its return type to Task):
public async Task InitiateSearchAsync()
(it's also a good idea to add an Async suffix to follow the common pattern).
Next, you'd add an await everywhere that InitiateSearchAsync is called, and so on.
I'm assuming Client.ListObjectsAsync returns a Task object, so a solution for your specific problem would be this:
public async void InitiateSearch()
{
//code
var collection = await Client.ListObjectsAsync(request, (responseObject) =>
{
//code
});
foreach (var item in collection)
{
//do stuff with item
}
}
the variable result will now be filled with the objects. You may want to set the return type of InitiateSearch() to Task, so you can await it too.
await InitiateSearch(); //like this
If this method is an event handler of some sort (like called by the click of a button), then you can keep using void as return type.
A simple introduction from an unpublished part of the documentation for async-await:
Three things are needed to use async-await:
The Task object: This object is returned by a method which is executed asynchronous. It allows you to control the execution of the method.
The await keyword: "Awaits" a Task. Put this keyword before the Task to asynchronously wait for it to finish
The async keyword: All methods which use the await keyword have to be marked as async
A small example which demonstrates the usage of this keywords
public async Task DoStuffAsync()
{
var result = await DownloadFromWebpageAsync(); //calls method and waits till execution finished
var task = WriteTextAsync(#"temp.txt", result); //starts saving the string to a file, continues execution right await
Debug.Write("this is executed parallel with WriteTextAsync!"); //executed parallel with WriteTextAsync!
await task; //wait for WriteTextAsync to finish execution
}
private async Task<string> DownloadFromWebpageAsync()
{
using (var client = new WebClient())
{
return await client.DownloadStringTaskAsync(new Uri("http://stackoverflow.com"));
}
}
private async Task WriteTextAsync(string filePath, string text)
{
byte[] encodedText = Encoding.Unicode.GetBytes(text);
using (FileStream sourceStream = new FileStream(filePath, FileMode.Append))
{
await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
}
}
Some thing to note:
You can specify a return value from an asynchronous operations with Task. The await keyword waits till the execution of the method finishes, and returns the string.
the Task object contains the status of the execution of the method, it can be used as any other variable.
if an exception is thrown (for example by the WebClient) it bubbles up at the first time the await keyword is used (in this example at the line string result (...))
It is recommended to name methods which return the Task object as MethodNameAsync
For more information about this take a look at http://blog.stephencleary.com/2012/02/async-and-await.html.
I want to read a XML file from the Web with following method.
public static async void Load_WinPhone(string URL)
{
HttpClient client = new HttpClient();
var httpResponseMessage = await client.GetAsync(new Uri(URL));
if (httpResponseMessage.StatusCode == System.Net.HttpStatusCode.OK)
{
var xmlStream = await httpResponseMessage.Content.ReadAsStreamAsync();
XDocument Xdoc = XDocument.Load(xmlStream);
var query = from data in Xdoc.Descendants("article")
select new MyClass
{
Title = data.Element("title").Value
}
foreach (MyClass x in query)
{
AnotherClass.List.Add(x);
}
}
This Works, but after the method finished the AnotherClass.List is still empty.
I think it is because of the async, I tried this in the console without the async and it worked fine.
But now i want to to this on a Windows Phone 8.1 and the list stays empty.
Can someone explain me why or even have a workaround for this?
Yes, that's how await works - from the point of view of the caller, it's basically the same thing as a return. So when you call this method, it most likely returns on the first await - long before AnotherClass.List is modified.
The main problem you have is that your method is async void - you're throwing away all the information about the method's execution. Instead, you want to return Task - this allows you to await the method or bind a continuation to it.
Whenever you break the await chain, you also break the synchronicity of the code. Most of the time (especially in UI), you want to await all the way to the top - usually, the only thing that's async void is the event handlers, and even then it's only because event handlers must return void.
Overall, multi-threading and asynchronous code is a rather big topic - http://www.albahari.com/threading/ is a great start on understanding most of the fundamentals, as well as ways to handle it well in C#.