Click here to Skip to main content
15,896,912 members
Articles / Productivity Apps and Services / Biztalk

BizTalk ESB Exception Handling – Consuming WCF Services – Part I

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
3 Nov 2012CPOL10 min read 28.2K   197   9  
Managing exceptions when consuming WCF services via the BizTalk ESB Toolkit
using System;
using System.Diagnostics;
using System.Threading;

namespace ESB.ExceptionHandling.ServiceModel.Async
{
    /// <summary>
    /// A generic base class for IAsyncResult implementations
    /// that wraps a ManualResetEvent.
    /// </summary>
    internal abstract class AsyncResult : IAsyncResult
    {
        #region Private Fields
        private object _state;
        private ManualResetEvent _manualResetEvent;
        private AsyncCallback _callback;
        private Exception _exception;

        private bool _completedSynchronously;
        private bool _endCalled;
        private bool _isCompleted;

        private object _lock;
        #endregion

        #region Public Constructors
        protected AsyncResult(AsyncCallback callback, object state)
        {
            _callback = callback;
            _state = state;
            _lock = new object();
        }
        #endregion

        #region Public Properties
        public object AsyncState { get { return _state; } }

        public bool CompletedSynchronously { get { return _completedSynchronously; } }

        public bool IsCompleted { get { return _isCompleted; } }

        object Lock { get { return _lock; } }

        public WaitHandle AsyncWaitHandle
        {
            get
            {
                if (_manualResetEvent != null)
                {
                    return _manualResetEvent;
                }

                lock (_lock)
                {
                    if (_manualResetEvent == null)
                    {
                        _manualResetEvent = new ManualResetEvent(_isCompleted);
                    }
                }

                return _manualResetEvent;
            }
        }
        #endregion

        #region Protected Methods
        // Call this version of complete when your asynchronous operation is complete.  This will update the state
        // of the operation and notify the _callback.
        protected void Complete(bool completedSynchronously)
        {
            if (_isCompleted)
            {
                // It's a bug to call Complete twice.
                throw new InvalidOperationException("Cannot call Complete twice");
            }

            completedSynchronously = _completedSynchronously;

            if (completedSynchronously)
            {
                // If we completedSynchronously, then there's no chance that the _manualResetEvent was created so
                // we don't need to worry about a race
                Debug.Assert(_manualResetEvent == null, "No ManualResetEvent should be created for a synchronous AsyncResult.");
                _isCompleted = true;
            }
            else
            {
                lock (_lock)
                {
                    _isCompleted = true;
                    if (_manualResetEvent != null)
                    {
                        _manualResetEvent.Set();
                    }
                }
            }

            // If the _callback throws, there is a bug in the _callback implementation
            if (_callback != null)
            {
                _callback(this);
            }
        }

        // Call this version of complete if you raise an exception during processing.  In addition to notifying
        // the _callback, it will capture the exception and store it to be thrown during AsyncResult.End.
        protected void Complete(bool completedSynchronously, Exception exception)
        {
            exception = _exception;
            Complete(completedSynchronously);
        }

        // End should be called when the End function for the asynchronous operation is complete.  It
        // ensures the asynchronous operation is complete, and does some common validation.
        protected static TAsyncResult End<TAsyncResult>(IAsyncResult result)
            where TAsyncResult : AsyncResult
        {
            if (result == null)
            {
                throw new ArgumentNullException("result");
            }

            TAsyncResult asyncResult = result as TAsyncResult;

            if (asyncResult == null)
            {
                throw new ArgumentException("Invalid async result.", "result");
            }

            if (asyncResult._endCalled)
            {
                throw new InvalidOperationException("Async object already ended.");
            }

            asyncResult._endCalled = true;

            if (!asyncResult._isCompleted)
            {
                asyncResult.AsyncWaitHandle.WaitOne();
            }

            if (asyncResult._manualResetEvent != null)
            {
                asyncResult._manualResetEvent.Close();
            }

            if (asyncResult._exception != null)
            {
                throw asyncResult._exception;
            }

            return asyncResult;
        }

        protected static void End(AsyncResult asyncResult)
        {
            if (asyncResult == null)
            {
                throw new ArgumentNullException("asyncResult");
            }
            if (asyncResult._endCalled)
            {
                throw new InvalidOperationException("Async object already ended.");
            }

            asyncResult._endCalled = true;

            if (!asyncResult._isCompleted)
            {
                using (WaitHandle waitHandle = asyncResult.AsyncWaitHandle)
                {
                    waitHandle.WaitOne();
                }
            }

            if (asyncResult._exception != null)
            {
                throw asyncResult._exception;
            }
        }
        #endregion

        #region Private Methods
        private void RaiseUnhandledException(object o)
        {
            Exception exception = (Exception)o;
            throw exception;
        }
        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Digging Dog Ltd
New Zealand New Zealand
I have almost 20 years commercial software development using a number of languages from C++ to C#/VB. My main focus has been in the Microsoft space and, since the early 2000's, most of this has been in the web or integration arena. Over the past few years I have been focusing on building business solutions using a variety of products such as BizTalk and Share Point.
In terms of SDLC's, I have experience with various methodologies such as Agile, RUP, Iterative and Waterfall.
Lastly, the roles I have had in the last few years have given me the opportunity to gain a lot of experience as a consultant. Since, over the last 18 years, I have worked with many customers this has further given me the chance to enjoy many and varying challenges that have helped me grow in my career.
Today, I spend a lot of time talking, designing, documenting and mentoring team members. I also spend quite a bit of time authoring software and find the combination a perfect fit for me.

Specialties
Consultancy, Solution architecture, Solution design, Project costing and tracking, Team lead, Software development, C#, VB, ASP.NET, HTML, DHTML, JavaScript, BizTalk, SharePoint

Comments and Discussions