Wednesday, March 14, 2012

Sample Code Implemented using EAP


EAP (Event Asynchronous Pattern), helps to invoke a function in another thread which enables the current thread to continue. This can be achieved using Threads as well, but EAP helps to raise an event which alerts the client or caller sending the result or error while performing action etc…
Below code is sample to explain EAP, taking calculator as an example.
Performing “Sum” is an operation in “Calculator” class. This function can be called directly which is regular just like synchronous.
Calculator class also has an another method “SumAsync”, which is called sending the same arguments as like with “Sum” but an extra argument which can be used in the “Async” or helps to map the asyn response with the asyn request.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Threading;
using System.Collections.Specialized;
namespace Calculator
{
    class Program
    {
        static void Main(string[] args)
        {
            Calculator objCalc = new Calculator();

            //an event to observe when the sum operation gets completed
            objCalc.OnSumCompleted += new EventHandler<SumCompletedEventArgs>(objCalc_OnSumCompleted);

            //invoking the sum async function sending the appropriate user state

            //Calling the async operations
            objCalc.SumAsync(5, 8, new { Desc = "Sum of 5 and 8" });
            //performing same asyn operations with same userstates to check
            //if throwing dduplicate operation error
            objCalc.SumAsync(5, 8, new { Desc = "Sum of 5 and 8" });
            objCalc.SumAsync(4, 8, new { Desc = "Sum of 4 and 8" });
            Console.Read();
        }

        static void objCalc_OnSumCompleted(object sender, SumCompletedEventArgs e)
        {
            //Checking if the operation has returened any error
            if(e.Error==null)
                //writing the result
            Console.WriteLine(sender.GetType().InvokeMember("Desc",
                System.Reflection.BindingFlags.GetProperty, null, sender, null) + " is " + e.Result);
            else
                //writing the error
                Console.WriteLine("Error occured while performing "+sender.GetType().InvokeMember("Desc", System.Reflection.BindingFlags.GetProperty, null, sender, null) + "\n" + e.Error.Message);
            Console.Read();
        }
    }

    //Sum completed event arguments
    public class SumCompletedEventArgs : AsyncCompletedEventArgs
    {
        public int Result;
        public SumCompletedEventArgs(Exception exp, bool Cancelled, object UserState)
            : base(exp, Cancelled, UserState)
        {
        }
    }

    //Calculator class which supports add operations in EAP and Synchronously
    public class Calculator
    {
        //Hybrid dictionary for thread safe and adding the request tasks
        HybridDictionary tasks = new HybridDictionary();

        //delegate to invoke Sum Operation completed event
        public delegate void SumCompletedEventHandler(SumCompletedEventArgs e);

        //Event which gets fired when sum operation is completed
        public event EventHandler<SumCompletedEventArgs> OnSumCompleted;

        //Function which sums up given numbers.
        //Sleep is included to observer asyn behaviour
        public int Sum(int a, int b)
        {
            Thread.Sleep(2000);
            return a + b;
        }

        //sum async method called sending "userstate" asn extra argument
        public void SumAsync(int a, int b, object UserState)
        {
            AsyncOperation asyncOp = null;
            try
            {
                //Checking if the request with given userstate is already in progress
                lock (tasks.SyncRoot)
                {
                    //creating async operation to track the async operation
                    asyncOp = AsyncOperationManager.CreateOperation(UserState);
                    if (tasks.Contains(UserState))
                        throw new Exception("User state is already in progress, duplicate operations cannot be performed");
                    tasks[UserState] = asyncOp;
                }

                //Invoking the function which sums up the values,
                Action<int, int, AsyncOperation> objSumWorker = new Action<int, int, AsyncOperation>(SumWorker);
                objSumWorker.BeginInvoke(a, b, asyncOp, null, null);
            }
            catch (Exception exp)
            {
                SumCompletedEventArgs e = new SumCompletedEventArgs(exp, false, UserState);
                asyncOp.PostOperationCompleted(
                    (o) =>
                    {
                        if (OnSumCompleted != null)
                            OnSumCompleted(o, e);
                    }, UserState
                    );
            }
        }

        private void SumWorker(int a, int b, AsyncOperation argAsync)
        {
            //creating an object for sumcompleted
            SumCompletedEventArgs e = new SumCompletedEventArgs(null, false, argAsync.UserSuppliedState);
            try
            {
                //finding the sum of given 2 numbers
                e.Result = Sum(a, b);

                //removing the request userstate from collection
                lock (tasks.SyncRoot)
                {
                    tasks.Remove(argAsync.UserSuppliedState);
                }

                //invoking anonymous function in asyn synchronisation context
                argAsync.PostOperationCompleted
                    (
                    (o) =>
                    {
                        if (OnSumCompleted != null)
                            OnSumCompleted(o, e);
                    }, argAsync.UserSuppliedState
                    );
            }
            catch (Exception exp)
            {
                e = new SumCompletedEventArgs(exp, false, argAsync.UserSuppliedState);
            }
        }
    }
}




No comments: