views:

98

answers:

2

I have a class that basically stores files in amazon s3. Here is what it looks like (simplified)

public class S3FileStore
{
   public void PutFile(string ID, Stream content)
      {
          //do stuff
      }
}

In my client app, I want to be able to call:

var s3 = new() S3FileStore();
s3.PutFile ("myId", File.OpenRead(@"C:\myFile1"));
s3.PutFile ("myId", File.OpenRead(@"C:\myFile2"));
s3.PutFile ("myId", File.OpenRead(@"C:\myFile3"));

I want this to be an asynchronous operation - I want the S3FileStore to handle this (i don't want my caller to have to execute PutFile asynchronously so to speak) but, i want to be able to trap exceptions / tell if the operation completed for each file.

I've looked at event based async calls, especially this: http://blogs.windowsclient.net/rendle/archive/2008/11/04/functional-shortcuts-2-event-based-asynchronous-pattern.aspx

However, I can't see how to call my PutFile (void) method?

Are there any better examples?

+1  A: 

Look at the solution for this question: http://stackoverflow.com/questions/2491842/adding-cancel-ability-and-exception-handling-to-async-code/2491923#2491923 . Hope it helps.

Nagg
+1  A: 

The BackgroundWorker base class might be worth a look, and also the Thread Pool:

ThreadPool.QueueUserWorkItem(delegate 
    { 
       s3.PutFile ("myId", File.OpenRead(@"C:\myFile1"));
    });

This is basically what you would do with the Action/BeginInvoke pattern. With BeginInvoke, you additionally receive an IAsyncResult on which you can call .WaitOne() to block the current thread until the operation finished, in case you need that. You would trigger a new BeginInvoke for every file you'd like to save.

If you need to do this frequently, a more sophisticated version could be to use a Queue in combination with the BackgroundWorker, e.g.:

public sealed class S3StoreLikePutFileWorker<TYourData> : BackgroundWorker
{
    private AutoResetEvent WakeUpEvent = new AutoResetEvent(false);

    private Queue<TYourData> DataQueue = new Queue<TYourData>();

    private volatile bool StopWork = false;

    public void PutFile(TYourData dataToWrite)
    {
        DataQueue.Enqueue(dataToWrite);
        WakeUpEvent.Set();
    }

    public void Close()
    {
        StopWork = true;
        WakeUpEvent.Set();
    }

    private override void OnDoWork(DoWorkEventArgs e)
    {
        do
        {
            // sleep until there is something to do
            WakeUpEvent.WaitOne();
            if(StopWork) break;

            // Write data, if available
            while(DataQueue.Count > 0)
            {
                TYourData yourDataToWrite = DataQueue.Dequeue();
                // write data to file
            }
        }
        while(!StopWork);
    }
}

Depending on how much complexity you need.

The BackgroundWorker supports progress feedback (set WorkerReportsProgress = true; in the constructor), and you can also add a custom event to report errors, if that is necessary:

// create a custom EventArgs class that provides the information you need
public sealed class MyEventArgs : EventArgs {
   // Add information about the file
}

// ... define the event in the worker class ...
public event EventHandler<MyEventArgs> ErrorOccured;

// ... call it in the worker class (if needed) ...
if(ErrorOccured != null) ErrorOccured(this, new MyEventArgs(/*...*/));

Markus