Parker Lewis

Hello everybody

I have a typical three layer architecture which consists of a presentation layer, a business layer and a data access layer. I have several webservices which I wrapped in the data access layer. The auto-generated webservice proxies contain asnyc methods for each webservice operation, i.e. they implement the event-based asynchronous pattern. Example: If you call the MyWsMethodAsync method, the operation is asynchronously executed on another thread. If the operation is completed the MyWsMethodCompleted event handler is called.

As I said, my DAL wraps the webservices and exposes it's own methods which then call the webservice methods (e.g. MyDALMethod calls the MyWsMethod). I'd like to expose the async methods too. My question is, how can I achieve this in the most easiest way Do I have to define a MyDALMethodAsync method which just calls the MyWsMethodAsync method then And when the MyWsMethodCompleted event handler is executed within the DAL I just fire the MyDALCompleted event which will then be handled within the business layer

Maybe this is already the solution, but it seems that I have to do the same thing in the business layer classes again until I can make use of the async behavior from within the GUI. Is this right Or is there another way to achieve this If not, then it sounds like a lot of writing work.

Kind regards



Re: Architecture General How to implement the event-based asynchronous pattern in a three layer architecture?

Robert Rossney

Why (other than the fact that the WS proxies generated methods for you automatically) would you make the interface between the business and data-access layers asynchronous You really only use asynchronous WS methods in UIs that are calling the web service directly. In the scenario you're describing, the asynchronous interface is the one between presentation and business layers. The business layer can call the DAL synchronously.





Re: Architecture General How to implement the event-based asynchronous pattern in a three layer architecture?

Parker Lewis

Hi Robert

You're completely right. I didn't think very much. There's absolutely no need to make the interface between business layer and DAL asynchronous. Thanks a lot for revealing my error in reasoning : )

Would you then just implement the event-based asynchronous pattern in the business layer classes or would you maybe use the BackgroundWorker component in the GUI and pass an instance of it to the business layer classes (as described in the last section of the following blog: http://www.developerdotstar.com/community/node/671)

Thanks a lot.

Best wishes





Re: Architecture General How to implement the event-based asynchronous pattern in a three layer architecture?

Ollie Riches

Initially I would start off making the all the calls to the business logic layer synchronous and leave any asycnhronous requirement upto the user interface layer, this is not my preferred way but this will get you up and running faster. Ideally I would implement both an synchronous and an asynchronous event-driven pattern in the business logic layer.

HTH

Ollie Riches






Re: Architecture General How to implement the event-based asynchronous pattern in a three layer architecture?

Robert Rossney

I don't like passing the BackgroundWorker to a class. It's part of the form, and to my mind it belongs there.

My preferred way (which I should really write up at some point):

  • Create an abstract Task class that exposes a virtual Execute method.
  • Implement subclasses of Task which correspond to user actions that can launch a long-running method, and that each implement an Execute method to call those methods synchronously.
  • Create a Queue<Task> in the UI.
  • Make the UI options that launch these methods create the appropriate Task, enqueue it, and, if the queue has exactly one item in it, call the BackgroundWorker's DoWork method.
  • Make the DoWork method grab the next Task from the Queue and call its Execute method.
  • Make the RunWorkerCompleted method dequeue the Task and then, if the Queue contains any more tasks, launch the DoWork method again.

Advantages of this approach:

  • It's not hard to do. The hardest part is coming up with the idea.
  • You can queue long-running operations. Ordinarily, you can't start an asynchronous operation if there's already one running unless you create multiple BackgroundWorkers.
  • You can manage the queue. You can display queue items, give the user the ability to cancel tasks that are waiting, etc. Also (and this can be quite useful) it's easy to write code that prevents the user from running the same task twice.
  • The amount of BackgroundWorker code - code that always has to execute in a separate thread - is extremely small. Most of the code you end up writing lives in classes that you can test in an ordinary single-threaded test harness like NUnit.

You do have to lock the queue while you're enqueueing and dequeuing tasks and testing the queue's length, to close a tiny window of vulnerability, but that's no big deal.





Re: Architecture General How to implement the event-based asynchronous pattern in a three layer architecture?

Parker Lewis

Hmm.. I'm not sure if I did understand everything... do you have a code sample

Regards





Re: Architecture General How to implement the event-based asynchronous pattern in a three layer architecture?

Robert Rossney

Let's see what I can do here. This is more or less off the top of my head, so it'll probably require debugging or worse. (I have this model working in some production code, but it's been extended so heavily that the underlying simplicity is hard to see.)

The basic idea is that you want to keep the code in the BackgroundWorker's event handlers simple. All of the code that actually does work lives outside the form, in its own class(es). First we have the abstract Task class:

Code Snippet

public abstract class Task

{

public abstract void Execute();

}

This is a simple pattern that lets you implement different classes with different code in their Execute methods, e.g.:

Code Snippet

public class Task1 : Task

{

public void Execute()

{

// actual code that does something long-running goes here

}

}

public class Task2 : Task

{

public void Execute()

{

// this performs some other flavor of long-running task

}

}

This all exists to support this sort of code in your form class:

Code Snippet

private Queue<Task> TaskQueue = new Queue<Task>();

...

launchTask1Button_Click(object sender, EventArgs e)

{

LaunchTask(new Task1());

}

launchTask2Button_Click(object sender, EventArgs e)

{

LaunchTask(new Task2());

}

// This launches a long-running task:

private void LaunchTask(Task t)

{

// In a fully working implementation, you'd search through

// the queue to determine whether the task that the user

// was trying to launch is already in the queue because he

// already clicked the launch button and it hasn't finished

// (or started) yet. This just indiscriminately adds the

// task to the queue.

lock (TaskQueue)

{

TaskQueue.Enqueue(t);

}

// Only launch the BackgroundWorker if the queue

// was empty; otherwise, the RunWorkerComplete event

// handler will take care of it.

if (TaskQueue.Count == 1)

{

backgroundWorker.RunWorkerAsync();

}

}

backgroundWorker_DoWork(object sender, DoWorkEventArgs e)

{

// get the task to perform from the queue, but don't

// dequeue it.

Task t = TaskQueue.Peek();

// note that the BackgroundWorker doesn't care what the

// task is or what it does; it's just calling the one

// method it knows about:

t.Execute();

}

backgroundWorker_RunWorkerComplete(object sender, RunWorkerCompletedEventArgs e)

{

// Grownups check e.Error here and do something useful if the task

// threw an exception. Alternatively, you can build exception

// handling into the abstract Task class.

// The task at the head of the queue is done, so dequeue it

lock(TaskQueue)

{

TaskQueue.Dequeue();

}

// Have any more tasks been enqueued since while the one

// that just finished was running if so, repeat.

if (TaskQueue.Count != 0)

{

backgroundWorker.RunWorkerAsync();

}

}

This is a lot of work if you only have one kind of long-running task, but if you have two, or thirty, it's another matter. If you need to, you can build methods to manage the queue. If you need to give the user a visualization of the queue, you can extend the Task class to include a Description field. You can let the user remove tasks from the queue, too, just so long as you lock the queue while you're actually removing them.

It's also easy to extend this model to include things like progress and status reporting. Add this to the Task class:

Code Snippet

public string Status;

public System.Windows.Forms.Control NotificationTarget;

public System.EventHandler NotificationDelegate;

private void Notify()

{

if (NotificationTarget == null) return;

if (NotificationDelegate == null) return;

NotificationTarget.Invoke(NotificationDelegate, this, new EventArgs());

}

and add this your form:

Code Snippet

private void Form1_Notify(object sender, EventArgs e)

{

Task t = (Task)sender;

statusLabel1.Text = t.Status;

}

and put this code into LaunchTask:

Code Snippet

t.NotificationTarget = this;

t.NotificationDelegate = (System.EventHandler)Form1_Notify;

Now whenever code in the Execute() method needs to notify the user that something's happening, you just do this:

Code Snippet

Status = "Something's happening!";

Notify();