Reputation: 17915
I have custom control and I have interface this control exposes to it's users.
public interface ILookupDataProvider
{
string IdColumnName { get; }
IEnumerable<IDataColumn> Metadata { get; set; }
void GetDataAsync(string parameters,
Action<IEnumerable<object>> onSuccess, Action<Exception> onError);
}
So, it's my attempt to expose async operation in GetDataAsync
But I don't know how to implement this method in my class that implements interface. I understand this portion as I have method which will execute and then onCompletion
, onSucess
or onError
delegate will be called.
Can somebody help with syntax on how to write those?
EDIT:
It's 4.0 and I can't use await
commands
EDIT 2:
I use DevForce framework to load data, but for the sake of this sample - let's do WCF service for example. How would I wrap WCF service call in my interface implementation?
Also, do you think it's OK to create interface like this to present async operation? Would you do it differently with event for example?
Upvotes: 4
Views: 491
Reputation: 13545
To use tasks you can use it like this. The only tricky thing to remember is to execute the callback in your UI thread which is achieved with TaskScheduler.FromCurrentSynchronizationContext() so you can update your UI or display a messagebox if something went wrong.
Due to the event driven nature of this stuff it can happen that if you hammer the button which does start the WCF calls that you get the results not back in the order you did send the requests. You can prevent this by storing the started task and cancel the last started task if you want to start a new operation or you can simply ignore the subsequent requests while a task is running.
private void button1_Click(object sender, EventArgs e)
{
GetDataAsync("www.data.com").ContinueWith(result =>
{
if (result.Exception != null)
{
MessageBox.Show(this, "Error: {0}" + result.Exception, "Error");
}
else
{
foreach (var obj in result.Result)
{
textBox1.Text += obj.ToString();
}
}
},
TaskScheduler.FromCurrentSynchronizationContext()
);
}
Task<IEnumerable<object>> GetDataAsync(string parameters)
{
return Task<IEnumerable<object>>.Factory.StartNew(() =>
{
Thread.Sleep(500);
// throw new ArgumentException("uups");
// make wcf call here
return new object[] { "First", "second" };
});
}
Upvotes: 1
Reputation: 74560
You really don't want to use this pattern:
void GetDataAsync(string parameters,
Action<IEnumerable<object>> onSuccess, Action<Exception> onError);
Instead, you want to use this:
Task GetDataAsync(string parameters);
In returning a Task
, you are returning an instance which represents the asynchronous unit of work. From there, the consumer of the API can choose to call ContinueWith
and decide what to do when it succeeds, or if there is an error.
However, there is a design flaw in your example. In a method named GetDataAsync
, I'd expect data to be returned. That's not a problem, you can just change the signature to:
Task<MyData> GetDataAsync(string parameters);
This now returns a Task<T>
which you can use the Result
property of to get the result (it will block if the task isn't done), or you can use the ContinueWith
method again to process the data when the async operation is done.
Additionally, you can take a CancellationToken
structure instance of a parameter to determine if you should cancel your operation:
Task<MyData> GetDataAsync(string parameters,
CancellationToken cancellationToken);
Again, ContinueWith
will allow you to indicate the action to take on cancellation.
Note that this is the way how methods using await
and async
in the Async CTP are currently being modeled; they are returning Task
or Task<T>
; doing the same in your code will allow you to be ready when these language features are baked in.
Upvotes: 3
Reputation: 755457
The basic idea here is that the query will occur an a background thread. Once your operation is complete you would use the onSuccess
and onError
callbacks in order to report the new values. For example
void GetDataAsync(
string parameters,
Action<IEnumerable<object>> onSuccess,
Action<Exception> onError) {
WaitCallback doWork = delegate {
try {
IEnumerable<object> enumerable = GetTheData(parameters);
onSuccess(enumerable);
} catch (Exception ex) {
onError(ex);
}
};
ThreadPool.QueueUserWorkItem(doWork, null);
}
Upvotes: 6