Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Need to wrap my head around Async operations

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?

like image 725
katit Avatar asked Dec 27 '22 11:12

katit


2 Answers

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);
}
like image 160
JaredPar Avatar answered Jan 04 '23 01:01

JaredPar


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.

like image 24
casperOne Avatar answered Jan 04 '23 02:01

casperOne