How can I use async/await to call a webservice? - c#

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

Related

Waiting for events asynchronously in C#

I'm working on a WCF service/client trying to figure out how to replace a ManualResetEvent with something that won't block the caller thread.
Most important is that await client.CloseAsync() won't be called until the FinishedEventReceived event has been fired.
I've looked at using a TaskCompletionSource but I'm somewhat unsure how that would work in this case.
I know the code is a bit ugly and totally defeats the purpose of using asynchrounously programming, my apoligies.
Any ideas?
private async Task CallServiceMethodAndWaitForEvent()
{
var mre = new ManualResetEvent(true);
var client = new AwesomeClient();
client.FinishedEventReceived += (s, e) =>
{
// Do something with the result, this event is only fired once.
mre.Set();
};
client.UpdateEventReceived += (s, e) =>
{
// This even can fire several times before the finished event.
};
try
{
var parameters = new Parameters()
{
SomeParameter = "Test123",
TestAmount = 10000,
};
var errors = await client.DoWorkAsync(parameters);
Debug.WriteLine(errors);
mre.WaitOne(TimeSpan.FromSeconds(20));
await client.CloseAsync();
}
catch (FaultException ex)
{
}
catch (Exception)
{
client.Abort();
}
}
Probably the simplest way to do what you want would be to replace the ManualResetEvent with - as you mentioned - a TaskCompletionSource. For example:
var tcs = new TaskCompletionSource<int>();
var client = new AwesomeClient();
client.FinishedEventReceived += (s, e) =>
{
// Do something with the result, this event is only fired once.
tcs.SetResult(42); // number here is a dummy, since you only want Task
};
...
await tcs.Task;
await client.CloseAsync();
Note that the timeout aspect is harder; a common approach there is to use Task.Delay as a fallback, and Task.WhenAny, i.e.
var timeout = Task.Delay(timeoutInterval);
if (timeout == await Task.WhenAny(timeout, tcs.Task))
throw new TimeoutException();
It looks like you are working with some class that implements the Event-based Asynchronous Pattern. What you really want, if you're doing async, is to work with APIs that implement the Task-based Asynchronous Pattern.
Thankfully, Microsoft offer specific guidance on adapting EAP to look like TAP:
Wrapping an Event-based Asynchronous Pattern (EAP) implementation is more involved than wrapping an APM pattern, because the EAP pattern has more variation and less structure than the APM pattern. To demonstrate, the following code wraps the DownloadStringAsync method. DownloadStringAsync accepts a URI, raises the DownloadProgressChanged event while downloading in order to report multiple statistics on progress, and raises the DownloadStringCompleted event when it's done. The final result is a string that contains the contents of the page at the specified URI.
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;
}
Which hopefully you can adapt to the specific API you're working with.

Instructing method to return only after anonymous event handler finishes

Inside a static method I am using WebBrowser. I am subscribing to its Navigated event an anonymous method.Can I instruct my method to return only after WebBroswer fires a Navigated event and my anonymous handler finishes?
using forms = System.Windows.Forms;
namespace ProxyProvider
{
public delegate string finished();
public static class ProxyProvider
{
public static string GetProxy()
{
string returnValue = "";
finished meFinished = () =>
{
return returnValue;
};
forms.WebBrowser browser = new forms.WebBrowser();
try
{
browser.Navigated += (s, e) =>
{
if (browser.ReadyState == forms.WebBrowserReadyState.Loading) //waiting for browser finishes loading page
return;
returnValue = ParseHtml(browser.DocumentText);
meFinished(); //I want this function to return only here. After it finishes parsing
};
browser.Navigate("http://example.com/proxy-list/");
return returnValue; // don't want to return it here
}
catch (Exception e)
{
forms.MessageBox.Show("ProxyProvider.GetProxy(): " + e.Message);
return "";
}
}
}
}
You can, but you'll have to adjust a few things.
The basic concept here is that your primary method will block until a value is set from the event handler. Since you are on the UI thread at the moment, you will want this to be done asynchronously (so your window remains responsive).
First we need to change the signature of your method:
public static Task<string> GetProxy()
We will return a Task object here so the caller can await us and get the string return value. Then you need to set up a TaskCompletionSource and set it in the handler:
TaskCompletionSource<string> navTaskSource= new TaksCompletionSource<string>();
browser.Navigated += (s, e) =>
{
if (browser.ReadyState == forms.WebBrowserReadyState.Loading)
return;
string returnValue = ParseHtml(browser.DocumentText);
navTaskSource.TrySetResult(returnValue);
};
Now that you have done that, just return the task:
browser.Navigate("http://example.com/proxy-list/");
return navTaskSource.Task;
Your calling code should await this method to avoid any UI thread blocking and get the return value.
string proxy = await MyClass.GetProxy();
Note that the method containing the await must be marked async.

Wait return from async task method

I'm beginner in code and I have spent hours of searching the solution to my problem.
I'm on a silverlight application which works with different web services and I call them with asynchronous method (I have no choice cause I am using silverlight), and the goal was too wait the return of my method before continue the execution of the application.
This is how my methods look (they are in a static class named Services) :
private static Task<ObservableCollection<Demande_GetNb_Result>> DemandeGetNbAsync()
{
TaskCompletionSource<ObservableCollection<Demande_GetNb_Result>> tcs = new TaskCompletionSource<ObservableCollection<Demande_GetNb_Result>>();
ABClient aBClient = new ABClient();
aBClient.Demande_GetNbCompleted += (sender, e) =>
{
if (e.Error != null)
tcs.TrySetException(e.Error);
else if (e.Cancelled)
tcs.TrySetCanceled();
else
tcs.TrySetResult(e.Result);
};
aBClient.Demande_GetNbAsync(((App)Application.Current).User.IdUser, true, true, true, (int)Constantes.Statut.Is_Waiting);
return tcs.Task;
}
public static async Task StartFamille()
{
ListInfos = await DemandeGetNbAsync(); // ListInfos is a Static variable on my Services class
}
and I call this method in other class like this :
var result = Services.StartFamille();
I want to wait until result has value before continuing the execution, but it doesn't work and I can't find a simple solution that I understand. The execution continue without waiting that a value is assigned to result.
Your method returns Task. Try to add await operator.
var result = await Services.StartFamille();
Moreover, DemandeGetNbAsync method should return variable of ObservableCollection<Demande_GetNb_Result> type and use await operator as well.

How to wait for a single event in C#, with timeout and cancellation

So my requirement is to have my function wait for the first instance an event Action<T> coming from another class and another thread, and handle it on my thread, allowing the wait to be interrupted by either timeout or CancellationToken.
I want to create a generic function I can reuse. I managed to create a couple options that do (I think) what I need, but both seem more complicated than I'd imagine it should have to be.
Usage
Just to be clear, a sample use of this function would look like this, where serialDevice is spitting out events on a separate thread:
var eventOccurred = Helper.WaitForSingleEvent<StatusPacket>(
cancellationToken,
statusPacket => OnStatusPacketReceived(statusPacket),
a => serialDevice.StatusPacketReceived += a,
a => serialDevice.StatusPacketReceived -= a,
5000,
() => serialDevice.RequestStatusPacket());
Option 1—ManualResetEventSlim
This option isn't bad, but the Dispose handling of the ManualResetEventSlim is messier than it seems like it should be. It gives ReSharper fits that I'm accessing modified/disposed things within the closure, and it's genuinely hard to follow so I'm not even sure it's correct. Maybe there's something I'm missing that can clean this up, which would be my preference, but I don't see it offhand. Here's the code.
public static bool WaitForSingleEvent<TEvent>(this CancellationToken token, Action<TEvent> handler, Action<Action<TEvent>> subscribe, Action<Action<TEvent>> unsubscribe, int msTimeout, Action initializer = null)
{
var eventOccurred = false;
var eventResult = default(TEvent);
var o = new object();
var slim = new ManualResetEventSlim();
Action<TEvent> setResult = result =>
{
lock (o) // ensures we get the first event only
{
if (!eventOccurred)
{
eventResult = result;
eventOccurred = true;
// ReSharper disable AccessToModifiedClosure
// ReSharper disable AccessToDisposedClosure
if (slim != null)
{
slim.Set();
}
// ReSharper restore AccessToDisposedClosure
// ReSharper restore AccessToModifiedClosure
}
}
};
subscribe(setResult);
try
{
if (initializer != null)
{
initializer();
}
slim.Wait(msTimeout, token);
}
finally // ensures unsubscription in case of exception
{
unsubscribe(setResult);
lock(o) // ensure we don't access slim
{
slim.Dispose();
slim = null;
}
}
lock (o) // ensures our variables don't get changed in middle of things
{
if (eventOccurred)
{
handler(eventResult);
}
return eventOccurred;
}
}
Option 2—polling without a WaitHandle
The WaitForSingleEvent function here is much cleaner. I'm able to use ConcurrentQueue and thus don't even need a lock. But I just don't like the polling function Sleep, and I don't see any way around it with this approach. I'd like to pass in a WaitHandle instead of a Func<bool> to clean up Sleep, but the second I do that I've got the whole Dispose mess to clean up again.
public static bool WaitForSingleEvent<TEvent>(this CancellationToken token, Action<TEvent> handler, Action<Action<TEvent>> subscribe, Action<Action<TEvent>> unsubscribe, int msTimeout, Action initializer = null)
{
var q = new ConcurrentQueue<TEvent>();
subscribe(q.Enqueue);
try
{
if (initializer != null)
{
initializer();
}
token.Sleep(msTimeout, () => !q.IsEmpty);
}
finally // ensures unsubscription in case of exception
{
unsubscribe(q.Enqueue);
}
TEvent eventResult;
var eventOccurred = q.TryDequeue(out eventResult);
if (eventOccurred)
{
handler(eventResult);
}
return eventOccurred;
}
public static void Sleep(this CancellationToken token, int ms, Func<bool> exitCondition)
{
var start = DateTime.Now;
while ((DateTime.Now - start).TotalMilliseconds < ms && !exitCondition())
{
token.ThrowIfCancellationRequested();
Thread.Sleep(1);
}
}
The question
I don't particularly care for either of these solutions, nor am I 100% sure either of them are 100% correct. Is either one of these solutions better than the other (idiomaticity, efficiency, etc), or is there an easier way or built-in function to meet what I need to do here?
Update: Best answer so far
A modification of the TaskCompletionSource solution below. No long closures, locks, or anything required. Seems pretty straightforward. Any errors here?
public static bool WaitForSingleEvent<TEvent>(this CancellationToken token, Action<TEvent> onEvent, Action<Action<TEvent>> subscribe, Action<Action<TEvent>> unsubscribe, int msTimeout, Action initializer = null)
{
var tcs = new TaskCompletionSource<TEvent>();
Action<TEvent> handler = result => tcs.TrySetResult(result);
var task = tcs.Task;
subscribe(handler);
try
{
if (initializer != null)
{
initializer();
}
task.Wait(msTimeout, token);
}
finally
{
unsubscribe(handler);
// Do not dispose task http://blogs.msdn.com/b/pfxteam/archive/2012/03/25/10287435.aspx
}
if (task.Status == TaskStatus.RanToCompletion)
{
onEvent(task.Result);
return true;
}
return false;
}
Update 2: Another great solution
Turns out that BlockingCollection works just like ConcurrentQueue but also has methods accepting a timeout and cancellation token. One nice thing about this solution is that it can be updated to make a WaitForNEvents fairly easily:
public static bool WaitForSingleEvent<TEvent>(this CancellationToken token, Action<TEvent> handler, Action<Action<TEvent>> subscribe, Action<Action<TEvent>> unsubscribe, int msTimeout, Action initializer = null)
{
var q = new BlockingCollection<TEvent>();
Action<TEvent> add = item => q.TryAdd(item);
subscribe(add);
try
{
if (initializer != null)
{
initializer();
}
TEvent eventResult;
if (q.TryTake(out eventResult, msTimeout, token))
{
handler(eventResult);
return true;
}
return false;
}
finally
{
unsubscribe(add);
q.Dispose();
}
}
You can use TaskCompletetionSource to create a Task that you can mark as completed or cancelled. Here's a possible implementation for a specific event:
public Task WaitFirstMyEvent(Foo target, CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<object>();
Action handler = null;
var registration = cancellationToken.Register(() =>
{
target.MyEvent -= handler;
tcs.TrySetCanceled();
});
handler = () =>
{
target.MyEvent -= handler;
registration.Dispose();
tcs.TrySetResult(null);
};
target.MyEvent += handler;
return tcs.Task;
}
In C# 5 you can use it like this:
private async Task MyMethod()
{
...
await WaitFirstMyEvent(foo, cancellationToken);
...
}
If you want to wait for the event synchronously, you can also use the Wait method:
private void MyMethod()
{
...
WaitFirstMyEvent(foo, cancellationToken).Wait();
...
}
Here's a more generic version, but it still works only for events with Action signature:
public Task WaitFirstEvent(
Action<Action> subscribe,
Action<Action> unsubscribe,
CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<object>();
Action handler = null;
var registration = cancellationToken.Register(() =>
{
unsubscribe(handler);
tcs.TrySetCanceled();
});
handler = () =>
{
unsubscribe(handler);
registration.Dispose();
tcs.TrySetResult(null);
};
subscribe(handler);
return tcs.Task;
}
You can use it like this:
await WaitFirstEvent(
handler => foo.MyEvent += handler,
handler => foo.MyEvent -= handler,
cancellationToken);
If you want it to work with other event signatures (e.g. EventHandler), you will have to create separate overloads. I don't think there's an easy way to make it work for any signature, especially since the number of parameters isn't always the same.
You can use Rx to convert the event to an observable, then to a task, and finally wait on that task with your token/timeout.
One advantage this has over any of the existing solutions, is that it calls unsubscribe on the event's thread, ensuring that your handler won't be called twice. (In your first solution you work around this by tcs.TrySetResult instead of tcs.SetResult, but it's always nice to get rid of a "TryDoSomething" and simply ensure DoSomething always works).
Another advantage is the code's simplicity. It's essentially one line. So you don't even particularly need an independent function. You can inline it so that it's more clear what exactly your code does, and you can make variations on the theme without needing a ton of optional parameters (like your optional initializer, or allow waiting on N events, or foregoing timeouts/cancellation in instances where they're not necessary). And you'd have both the bool return val and the actual result in scope when it's finished, if that's useful at all.
using System.Reactive.Linq;
using System.Reactive.Threading.Tasks;
...
public static bool WaitForSingleEvent<TEvent>(this CancellationToken token, Action<TEvent> onEvent, Action<Action<TEvent>> subscribe, Action<Action<TEvent>> unsubscribe, int msTimeout, Action initializer = null) {
var task = Observable.FromEvent(subscribe, unsubscribe).FirstAsync().ToTask();
if (initializer != null) {
initializer();
}
try {
var finished = task.Wait(msTimeout, token);
if (finished) onEvent(task.Result);
return finished;
} catch (OperationCanceledException) { return false; }
}
many thanks!
for helping other to understand...
(maybe showing serialdevice code with hits action handler code)
you could also put a generic type constrain adding something like
where TEvent : EventArgs
in my case i also need the result out of event in the "waiter"
so i changed signature like
(fast and ugly on a generic object...)
public static bool WaitForSingleEventWithResult<TEvent, TObjRes>(
this CancellationToken token,
Func<TEvent, TObjRes> onEvent,
...
calling it in this way
var ct = new CancellationToken();
object result;
bool eventOccurred = ct.WaitForSingleEventWithResult<MyEventArgs, object>(
onEvent: statusPacket => result = this.OnStatusPacketReceived(statusPacket),
subscribe: sub => cp.StatusPacketReceived_Action += sub,
unsubscribe: unsub => cp.StatusPacketReceived_Action -= unsub,
msTimeout: 5 * 1000,
initializer: /*() => serialDevice.RequestStatusPacket()*/null);
anyway... many thanks!
Why not just use
ManualResetEventSlim.Wait (int millisecondsTimeout, CancellationToken cancellationToken)
?

How to write a wrapper around an asynchronous method?

I have a JSON API which I want my application to access. So I wrote a method.
public List<Books> GetBooks()
{
var webclient = new WebClient();
var jsonOutput = webclient.DownloadString(
new Uri("http://someplace.com/books.json")
);
return ParseJSON(jsonOutput);//Some synchronous parsing method
}
Now I need to change DonwloadString to DownloadStringAsync.
I found this tutorial.
But this just seems too complicated. I'm trying to get this working, but am not sure if this is the right way to go. Perhaps there is a simpler and better way?
All of the async operations that require you to subscribe to events to get the results are just painful. I think that the simplest way to go is to abstract away the event handling into some nice extension methods and use continuation passing style (CPS) to process the results.
So, the first thing is to create an extension method for downloading strings:
public static void DownloadString(this Uri uri, Action<string> action)
{
if (uri == null) throw new ArgumentNullException("uri");
if (action == null) throw new ArgumentNullException("action");
var webclient = new WebClient();
DownloadStringCompletedEventHandler handler = null;
handler = (s, e) =>
{
var result = e.Result;
webclient.DownloadStringCompleted -= handler;
webclient.Dispose();
action(result);
};
webclient.DownloadStringCompleted += handler;
webclient.DownloadStringAsync(uri);
}
This method hides away the creation of the WebClient, all of the event handling, and the disposing and unsubscribing to clean things up afterwards.
It's used like this:
var uri = new Uri("http://someplace.com/books.json");
uri.DownloadString(t =>
{
// Do something with the string
});
Now this can be used to create a GetBooks method. Here it is:
public void GetBooks(Uri uri, Action<List<Books>> action)
{
if (action == null) throw new ArgumentNullException("action");
uri.DownloadString(t =>
{
var books = ParseJSON(t);
action(books);
});
}
It's used like this:
this.GetBooks(new Uri("http://someplace.com/books.json"), books =>
{
// Do something with `List<Books> books`
});
That should be neat and simple.
Now, you may wish to extend this a couple of ways.
You could create an overload of ParseJSON that has this signature:
void ParseJSON(string text, Action<List<Books>> action)
Then you could do away with the GetBooks method altogether and just write this:
var uri = new Uri("http://someplace.com/books.json");
uri.DownloadString(t => ParseJSON(t, books =>
{
// Do something with `List<Books> books`
// `string t` is also in scope here
}));
Now you have a nice neat fluent-style, composable set of operations. As a bonus the downloaded string, t, is also in scope so you can easily log it or do some other processing if need be.
You may also need to handle exceptions and these can be added like so:
public static void DownloadString(
this Uri uri,
Action<string> action,
Action<Exception> exception)
{
if (uri == null) throw new ArgumentNullException("uri");
if (action == null) throw new ArgumentNullException("action");
var webclient = (WebClient)null;
Action<Action> catcher = body =>
{
try
{
body();
}
catch (Exception ex)
{
ex.Data["uri"] = uri;
if (exception != null)
{
exception(ex);
}
}
finally
{
if (webclient != null)
{
webclient.Dispose();
}
}
};
var handler = (DownloadStringCompletedEventHandler)null;
handler = (s, e) =>
{
var result = (string)null;
catcher(() =>
{
result = e.Result;
webclient.DownloadStringCompleted -= handler;
});
action(result);
};
catcher(() =>
{
webclient = new WebClient();
webclient.DownloadStringCompleted += handler;
webclient.DownloadStringAsync(uri);
});
}
You can then replace the non-error handling DownloadString extension method with:
public static void DownloadString(this Uri uri, Action<string> action)
{
uri.DownloadString(action, null);
}
And then to use the error handling method you would do this:
var uri = new Uri("http://someplace.com/books.json");
uri.DownloadString(t => ParseJSON(t, books =>
{
// Do something with `List<Books> books`
}), ex =>
{
// Do something with `Exception ex`
});
The end result should be fairly simple to use and read. I hope this helps.
Assuming you are aren't writing an ASP.NET app.
Have you looked into using a Background Worker component? For long running tasks that shouldn't tie up the UI it is a clean and easy way to get multithreading capabilites. For instance you can perform updates to the UI using the ProgressChanged Event and the background worker and the background worker class will ensure that the thread that created the BW is the one that executes the ProcessChanged and WorkComplete event. So if you made the BW from the UI and set it off to work then you can update the UI safely from there.
Here's a quick article from MS http://msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx
Another really good link http://www.albahari.com/threading/part3.aspx#_BackgroundWorker
--edit--
I looked at the link and what he appears to be doing is a full implementation of the Cooporative Cancellation pattern. This is where a background thread will support cancellation gracefully by routinely checking a variable and cancelling if it's true. The BW is an implementation of this pattern.
If you want something really simple then you can try just using a ThreadPool
ThreadPool.QueueUserWorkItem(DoWork);
public void DoWork(){
//Just remember that this code happens in a seperate thread so don't update
//the UI. It will throw an exception. You would need to call
//Form.BeginInvoke(UpdateFunction) in order to update the UI
DoSomethingInteresting();
}

Categories

Resources