_fbClient.GetCompleted += new EventHandler<FacebookApiEventArgs>(OnFetchPageNotification);
_fbClient.GetAsync(_kNotificationPath, new Dictionary<string, object> { { "access_token", _kPageAccessToken } });
How to convert above code into awaitable code in wp7:
object = await _fbClient.GetAsync(_kNotificationPath, new Dictionary<string, object> { { "access_token", _kPageAccessToken } });
I have CTP Installed and task parallel library also.
The Async CTP came with a document that describes how to adapt each existing pattern to the Task Based Async pattern. It says that the Event based one is more variable, but does give one example:
public static Task<string> DownloadStringAsync(Uri url)
{
var tcs = new TaskCompletionSource<string>();
var wc = new WebClient();
wc.DownloadStringCompleted += (s,e) =>
{
if (e.Error != null) tcs.TrySetException(e.Error);
else if (e.Cancelled) tcs.TrySetCanceled();
else tcs.TrySetResult(e.Result);
};
wc.DownloadStringAsync(url);
return tcs.Task;
}
Where the original function that's being wrapped is DownloadStringAsync, the parameters match the parameters being passed to this function, and DownloadStringCompleted is the event that is being monitored.
(The same document appears to be downloadable here - the above sample (and more description) are from "Tasks and the Event-based Asynchronous Pattern (EAP)")
Related
I have a webservice written in Yii (php framework).
I use C# and Visual Studio 2012 to develop a WP8 application. I added a service reference to my project (Add Service Reference). So I am able to use webservice functions.
client = new YChatWebService.WebServiceControllerPortTypeClient();
client.loginCompleted += client_loginCompleted; // this.token = e.Result;
client.loginAsync(this.username, this.password);
client.getTestCompleted += client_getTestCompleted;
client.getTestAsync(this.token);
function getTestAsync and loginAsync return void and both are asynchronous. Is it possible for the functions to return Task<T>? I would like to use async/await keywords in my program.
Assuming that loginAsync returns void, and loginCmpleted event fires when login is done, this is called the Event-based Asynchronous Pattern, or EAP.
To convert EAP to await/async, consult Tasks and the Event-based Asynchronous Pattern. In particular, you'll want to make use of the TaskCompletionSource to convert the event-based model to a Task-based model. Once you've got a Task-based model, you can use C# 5's sexy await feature.
Here's an example:
// Use LoginCompletedEventArgs, or whatever type you need out of the .loginCompleted event
// This is an extension method, and needs to be placed in a static class.
public static Task<LoginCompletedEventArgs> LoginAsyncTask(this YChatWebService.WebServiceControllerPortTypeClient client, string userName, string password)
{
var tcs = CreateSource<LoginCompletedEventArgs>(null);
client.loginCompleted += (sender, e) => TransferCompletion(tcs, e, () => e, null);
client.loginAsync(userName, password);
return tcs.Task;
}
private static TaskCompletionSource<T> CreateSource<T>(object state)
{
return new TaskCompletionSource<T>(
state, TaskCreationOptions.None);
}
private static void TransferCompletion<T>(
TaskCompletionSource<T> tcs, AsyncCompletedEventArgs e,
Func<T> getResult, Action unregisterHandler)
{
if (e.UserState == tcs)
{
if (e.Cancelled) tcs.TrySetCanceled();
else if (e.Error != null) tcs.TrySetException(e.Error);
else tcs.TrySetResult(getResult());
if (unregisterHandler != null) unregisterHandler();
}
}
Now that you've converted the Event-based async programming model to a Task-based one, you can now use await:
var client = new YChatWebService.WebServiceControllerPortTypeClient();
var login = await client.LoginAsyncTask("myUserName", "myPassword");
I've had to do this a couple of times over the last year and I've used both #Judah's code above and the original example he has referenced but each time I've hit on the following problem with both: the async call works but doesn't complete. If I step through it I can see that it will enter the TransferCompletion method but the e.UserState == tcs will always be false.
It turns out that web service async methods like the OP's loginAsync have two signatures. The second accepts a userState parameter. The solution is to pass the TaskCompletionSource<T> object you created as this parameter. This way the e.UserState == tcs will return true.
In the OP, the e.UserState == tcs was removed to make the code work which is understandable - I was tempted too. But I believe this is there to ensure the correct event is completed.
The full code is:
public static Task<LoginCompletedEventArgs> RaiseInvoiceAsync(this Client client, string userName, string password)
{
var tcs = CreateSource<LoginCompletedEventArgs>();
LoginCompletedEventHandler handler = null;
handler = (sender, e) => TransferCompletion(tcs, e, () => e, () => client.LoginCompleted -= handler);
client.LoginCompleted += handler;
try
{
client.LoginAsync(userName, password, tcs);
}
catch
{
client.LoginCompleted -= handler;
tcs.TrySetCanceled();
throw;
}
return tcs.Task;
}
Alternatively, I believe there is a tcs.Task.AsyncState property too that will provide the userState. So you could do something like:
if (e.UserState == taskCompletionSource || e.UserState == taskCompletionSource?.Task.AsyncState)
{
if (e.Cancelled) taskCompletionSource.TrySetCanceled();
else if (e.Error != null) taskCompletionSource.TrySetException(e.Error);
else taskCompletionSource.TrySetResult(getResult());
unregisterHandler();
}
This was what I tried initially as it seemed a lighter approach and I could pass a Guid rather than the full TaskCompletionSource object. Stephen Cleary has a good write-up of the AsyncState if you're interested.
While adding your service reference make sure you selected Generate Task based operations in Advanced section. this will create awaitable methods like LoginAsync returning Task<string>
(Copied from OP, per https://meta.stackexchange.com/a/150228/136378 )
Answer:
Following code seems to work.
internal static class Extension
{
private static void TransferCompletion<T>(
TaskCompletionSource<T> tcs, System.ComponentModel.AsyncCompletedEventArgs e,
Func<T> getResult)
{
if (e.Error != null)
{
tcs.TrySetException(e.Error);
}
else if (e.Cancelled)
{
tcs.TrySetCanceled();
}
else
{
tcs.TrySetResult(getResult());
}
}
public static Task<loginCompletedEventArgs> LoginAsyncTask(
this YChatWebService.WebServiceControllerPortTypeClient client,
string userName, string password)
{
var tcs = new TaskCompletionSource<loginCompletedEventArgs>();
client.loginCompleted += (s, e) => TransferCompletion(tcs, e, () => e);
client.loginAsync(userName, password);
return tcs.Task;
}
}
I call it this way
client = new YChatWebService.WebServiceControllerPortTypeClient();
var login = await client.LoginAsyncTask(this.username, this.password);
If you want to be able to await the methods, they should return Task. You cannot await a method that returns void. If you want them to return a value, like int they should return Task<int> then the method should return int.
public async Task loginAsync(string username, string password) {}
Then you can call
Task t = loginAsync(username, password);
//login executing
//do something while waiting
await t; //wait for login to complete
When I execute the following code:
public static async Task UploadFile(string serverPath, string pathToFile, string authToken)
{
serverPath = #"C:\_Series\S1\The 100 S01E03.mp4";
var client = new WebClient();
var uri = new Uri($"http://localhost:50424/api/File/Upload?serverPath={WebUtility.UrlEncode(serverPath)}");
client.UploadProgressChanged += UploadProgressChanged;
client.UploadFileCompleted += UploadCompletedCallback;
//client.UploadFileAsync(uri, "POST", pathToFile);
client.UploadFile(uri, "POST", pathToFile);
}
I get the exception:
System.Net.WebException: 'The remote server returned an error: (404)
Not Found.'
I'm not too worried about the 404, I'm busy tracing down why the WebClient can't find it, but my big concern is that if I call UploadFileAsync with the same uri, the method just executes as if nothing is wrong.
The only indication that something is wrong is that neither of the two event handlers is invoked. I strongly suspect that I don't get an exception because the async call is not async/await but event based, but then I would expect some kind of event or property that indicates an exception has occurred.
How is one supposed to use code that hides errors like this, especially network errors which are relatively more common, in production?
Why no error notification for UploadFileAsync with WebClient?
Citing WebClient.UploadFileAsync Method (Uri, String, String) Remarks
The file is sent asynchronously using thread resources that are automatically allocated from the thread pool. To receive notification when the file upload completes, add an event handler to the UploadFileCompleted event.
Emphasis mine.
You get no errors because it is being executed on another thread so as not to block the current thread. To see the error you can access it in the stated event handler via the UploadFileCompletedEventArgs.Exception.
I was curious as to why using WebClient and not HttpClient which is already primarily async, but then my assumption was because of the upload progress.
I would suggest wrapping the WebClient call with event handlers in a Task using a TaskCompletionSource to take advantage of TAP.
The following is similar to the examples provided here How to: Wrap EAP Patterns in a Task
public static async Task UploadFileAsync(string serverPath, string pathToFile, string authToken, IProgress<int> progress = null) {
serverPath = #"C:\_Series\S1\The 100 S01E03.mp4";
using (var client = new WebClient()) {
// Wrap Event-Based Asynchronous Pattern (EAP) operations
// as one task by using a TaskCompletionSource<TResult>.
var task = client.createUploadFileTask(progress);
var uri = new Uri($"http://localhost:50424/api/File/Upload?serverPath={WebUtility.UrlEncode(serverPath)}");
client.UploadFileAsync(uri, "POST", pathToFile);
//wait here while the file uploads
await task;
}
}
Where createUploadFileTask is a custom extension method used to wrap the Event-Based Asynchronous Pattern (EAP) operations of the WebClient as one task by using a TaskCompletionSource<TResult>.
private static Task createTask(this WebClient client, IProgress<int> progress = null) {
var tcs = new TaskCompletionSource<object>();
#region callbacks
// Specifiy the callback for UploadProgressChanged event
// so it can be tracked and relayed through `IProgress<T>`
// if one is provided
UploadProgressChangedEventHandler uploadProgressChanged = null;
if (progress != null) {
uploadProgressChanged = (sender, args) => progress.Report(args.ProgressPercentage);
client.UploadProgressChanged += uploadProgressChanged;
}
// Specify the callback for the UploadFileCompleted
// event that will be raised by this WebClient instance.
UploadFileCompletedEventHandler uploadCompletedCallback = null;
uploadCompletedCallback = (sender, args) => {
// unsubscribing from events after asynchronous
// events have completed
client.UploadFileCompleted -= uploadCompletedCallback;
if (progress != null)
client.UploadProgressChanged -= uploadProgressChanged;
if (args.Cancelled) {
tcs.TrySetCanceled();
return;
} else if (args.Error != null) {
// Pass through to the underlying Task
// any exceptions thrown by the WebClient
// during the asynchronous operation.
tcs.TrySetException(args.Error);
return;
} else
//since no result object is actually expected
//just set it to null to allow task to complete
tcs.TrySetResult(null);
};
client.UploadFileCompleted += uploadCompletedCallback;
#endregion
// Return the underlying Task. The client code
// waits on the task to complete, and handles exceptions
// in the try-catch block there.
return tcs.Task;
}
Going one step further and creating another extension method to wrap the upload file to make it await able...
public static Task PostFileAsync(this WebClient client, Uri address, string fileName, IProgress<int> progress = null) {
var task = client.createUploadFileTask(progress);
client.UploadFileAsync(address, "POST", fileName);//this method does not block the calling thread.
return task;
}
Allowed your UploadFile to be refactored to
public static async Task UploadFileAsync(string serverPath, string pathToFile, string authToken, IProgress<int> progress = null) {
using (var client = new WebClient()) {
var uri = new Uri($"http://localhost:50424/api/File/Upload?serverPath={WebUtility.UrlEncode(serverPath)}");
await client.PostFileAsync(uri, pathToFile, progress);
}
}
This now allow you to call the upload asynchronously and even keep track of the progress with your very own Progress Reporting (Optional)
For example if in an XAML based platform
public class UploadProgressViewModel : INotifyPropertyChanged, IProgress<int> {
public int Percentage {
get {
//...return value
}
set {
//...set value and notify change
}
}
public void Report(int value) {
Percentage = value;
}
}
Or using the out of the box Progress<T> Class
So now you should be able to upload the file without blocking the thread and still be able to await it, get progress notifications, and handle exceptions, provided you have a try/catch in place.
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;
}
}
I'm using the async/await pattern throughout my code. However, there's one API which used the event-based asyncronous pattern. I've read on MSDN, and several StackOverflow answers, that the way to do this is to use a TaskCompletionSource.
My code:
public static Task<string> Process(Stream data)
{
var client = new ServiceClient();
var tcs = new TaskCompletionSource<string>();
client.OnResult += (sender, e) =>
{
tcs.SetResult(e.Result);
};
client.OnError += (sender, e) =>
{
tcs.SetException(new Exception(e.ErrorMessage));
};
client.Send(data);
return tcs.Task;
}
And called as:
string result = await Process(data);
Or, for testing:
string result = Process(data).Result;
The method always returns very quickly, but neither of the events get triggered.
If I add tcs.Task.Await(); just before the return statement, it works, but this doesn't provide the asyncronous behavior I want.
I've compared with the various samples I've seen around the internet, but don't see any difference.
The problem lays in the fact that after your Process method terminates, your ServiceClient local variable is eligible for garbage collection and may be collected prior to the event firing, hence a race condition is in place.
To avoid this, I'd define ProcessAsync as an extension method on the type:
public static class ServiceClientExtensions
{
public static Task<string> ProcessAsync(this ServiceClient client, Stream data)
{
var tcs = new TaskCompletionSource<string>();
EventHandler resultHandler = null;
resultHandler = (sender, e) =>
{
client.OnResult -= resultHandler;
tcs.SetResult(e.Result);
}
EventHandler errorHandler = null;
errorHandler = (sender, e) =>
{
client.OnError -= errorHandler;
tcs.SetException(new Exception(e.ErrorMessage));
};
client.OnResult += resultHandler;
client.OnError += errorHandler;
client.Send(data);
return tcs.Task;
}
}
And consume it like this:
public async Task ProcessAsync()
{
var client = new ServiceClient();
string result = await client.ProcessAsync(stream);
}
Edit:
#usr points out that usually, IO operations should be the ones keeping the reference to whom invoked them alive, which isn't the case as we're seeing here. I agree with him that this behavior is a bit peculiar, and should probably signal of some sort of design/implementation problem of the ServiceClient object. I'd advise, if possible, to look up the implementation and see if there is anything that might be causing the reference to not stay rooted.
I guess I should answer this.
public static Task<string> Process(Stream data)
{
var handle = new AutoResetEvent(false);
var client = new ServiceClient();
var tcs = new TaskCompletionSource<string>();
client.OnResult += (sender, e) =>
{
tcs.SetResult(e.Result);
handle.Set();
};
client.OnError += (sender, e) =>
{
tcs.SetException(new Exception(e.ErrorMessage));
handle.Set();
};
client.Send(data);
handle.WaitOne(10000); // wait 10 secondds for results
return tcs.Task;
}
So basiacally, I'm creating a dll project which have to get some info from the bing maps api. Below I'm pasting a method which is resposible for getting and serializing the response:
private void GetResponse(Uri uri, Action<Response> callback)
{
WebClient wc = new WebClient();
wc.OpenReadCompleted += (o, a) =>
{
if (callback != null)
{
DataContractJsonSerializer responseSerializer = new DataContractJsonSerializer(typeof(Response));
callback(responseSerializer.ReadObject(a.Result) as Response);
}
};
wc.OpenReadAsync(uri);
}
Below method is using GetResponse method to get lattitudes:
public Lattitudes getLattitudes()
{
Lattitudes lattitudes = new Lattitudes();
GetResponse(geocodeRequestURI, (x) =>
{
lattitudes.SouthLatitude = x.ResourceSets[0].Resources[0].BoundingBox[0];
lattitudes.SouthLatitude = x.ResourceSets[0].Resources[0].BoundingBox[1];
lattitudes.SouthLatitude = x.ResourceSets[0].Resources[0].BoundingBox[2];
lattitudes.SouthLatitude = x.ResourceSets[0].Resources[0].BoundingBox[3];
});
return lattitudes;
}
My problem is that second method is returning empty object and part inside GetResponse method is being executed later. Is There a way to wait for this event to finish and then return, or maybe I need to redesign it ?(max version of .NET I can use is 4.0)
I suggest if you want to wait than make use of Task by converting method competable with async and await like as below
private async Task RequestDataAsync(string uri, Action<string> action)
{
var client = new WebClient();
string data = await client.OpenReadAsync(uri);
action(data);
}
call method from the code
Task t = RequestDataAsync(parameter)
t.Wait();
this help more : http://www.dotnetperls.com/async