views:

172

answers:

4

Hi, how do i write a async class like WebClient?

Is there anyway i can keep it short and does not have to repeat it for every method?

for example, i have:

Download(string a)
Download(string a, string b)

do i have to rewrite Async + Complete method for each of these?

Thank you very much.

+3  A: 

Update:

This example is for winforms, and I believe the OP is asking about asp.net. I will revise my answer once I've had some feedback from my comments.


You guessed it. I've found that an elegant way of multi-threading code in .Net is to use the BackgroundWorker class. It's really quite simple compared to implementing threads in say native C++.

The example on the MSDN page is maybe a little overwhelming to be honest, and you could sum it up like so (pseudo code - may not compile):

private BackgroundWorker bw;

private void foobar() {
  bw = new BackgroundWorker(); // should be called once, in ctor
  bw.DoWork += new DoWorkEventHandler(bw_DoWork);
  bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_Completed);

  int i = 0;
  bw.RunWorkerAsync(i);
}

private void bw_DoWork(object sender, DoWorkEventArgs e) {
  int i = (int)e.Argument;
  i++;
  e.Result = i;
}

private void bw_Completed(object sender, RunWorkerCompletedEventArgs e) {
  if (e.Error != null) {
    MessageBox.Show(e.Error.Message);
  } else {
    int i = (int)e.Result;
    MessageBox.Show(i.ToString());
  }
}

As for keeping repeated code to a minimum, you could use the same background worker for all methods I suppose, but you may be heading into smelly code territory with a big shiny golden hammer. Providing you only have a few methods, then creating event handlers for each operation shouldn't look too messy.

nbolton
I don't understand, if this was tagged 'asp.net', how could there be a messagebox, or is there really a actual messagebox for 'asp.net'...backgroundworker is more for winforms...
tommieb75
Yikes! What was I thinking!? Hmm, you're totally right, `BackgroundWorker` is not very useful at all for `Asp.Net`. I will update my answer with a more appropriate example.
nbolton
+1  A: 

If you want to call your synchronous method asynchronously you will need to use delegates. http://msdn.microsoft.com/en-us/library/2e08f6yc.aspx. With this approach you don't have to create Async and Complete methods.

The pattern you are describing is to use IAsyncResult in which case you will need to implement IAsyncResult (You could also use the one in the Framework depending on your requirements). Derive this in your class and implement the Async and Complete methods like you mentioned.

iaimtomisbehave
A: 

Use MethodInvoker.BeginInvoke to call a new thead.

Mr.Bua
+1  A: 

It sounds like you're asking about best practices for implementing a class which exposes methods that have asynchronous versions similar to how WebClient does?

It's worth noting that there's two patterns typically used in the .NET framework for classes that support asynchronous usage. For example, you'll typically see classes like Stream that expose BeginRead and EndRead. These use IAsyncResult and tend to be more complicated to use. (Overview on MSDN)

Then later on they came up with the event-based async pattern such as the one used by WebClient where you have a DownloadString/DownloadStringAsync method and a corresponding DownloadStringCompleted event. This is easier to work with for simple cases but isn't as flexible as the Begin/End pattern in my opinion.

So in your example, you have two overloaded methods. In this case I would simply design them such that the method with fewer parameters defers to the method with more parameters passing default values or nulls as needed. You can have multiple overloads of Download/DownloadAsync but you can only have one DownloadCompleted event which is fine.

Here's a very basic implementation. Notice that the only method really doing any work is one synchronous Download method. It's also important to note that this isn't the most efficient way of doing it either. If you wanted to take advantage of the async IO features of HttpWebRequest and such, this example would get complicated quickly. There's also an overly complex Async Operation pattern that I never liked one bit.

class Downloader {

    public void Download(string url, string localPath) {
        if (localPath == null) {
            localPath = Environment.CurrentDirectory;
        }
        // implement blocking download code
    }

    public void Download(string url) {
        Download(url, null);
    }

    public void DownloadAsync(string url, string localPath) {

        ThreadPool.QueueUserWorkItem( state => {

            // call the sync version using your favorite
            // threading api (threadpool, tasks, delegate.begininvoke, etc)
            Download(url, localPath);

            // synchronizationcontext lets us raise the event back on
            // the UI thread without worrying about winforms vs wpf, etc
            SynchronizationContext.Current.Post( OnDownloadCompleted, null );

        });

    }

    public void DownloadAsync(string url) {
        DownloadAsync(url, null);
    }

    private void OnDownloadCompleted(object state) {
        var handler = DownloadCompleted;
        if (handler != null) {
            handler(this, EventArgs.Empty);
        }
    }

    public event EventHandler DownloadCompleted;

}
Josh Einstein