I love Aspect Oriented Programming. The idea of centralizing the code of a specific nature (logging, performance counting/monitoring, exception handling, etc.) is very compelling form a clean code point of view. WCF allows me to get this "for free". I have been aspecting out my calls to WCF services for a while now, but I recently discovered a better way that allows for type safety and intellisense.

The calls used to look like this:

helper.CallService("GetSamplesByState", state);

But they now look like this:

helper.CallService(p => p.GetSamplesByState(state));

Here is the code with the changes highlighted:

using System;
using System.Reflection;
using System.ServiceModel;

namespace Common.Utilities
{
    public class CommunicationHelper<T> : IDisposable
        where T : class
    {
        ChannelFactory<T> factory;
        T channel;

        public CommunicationHelper(string channelName)
        {
            using (TimedLock.Lock(factory))
            {
                factory = new ChannelFactory<T>(channelName);
            }
        }

        public string Address
        {
            get
            {
                using (TimedLock.Lock(factory))
                {
                    return factory.Endpoint.Address.ToString();
                }
            }
        }

        private void EnsureChannelCreated()
        {
            using (TimedLock.Lock(factory))
            {
                if ((channel == null) ||
                    (((IClientChannel)channel).State == CommunicationState.Faulted))
                {
                    channel = factory.CreateChannel();
                }
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool isDisposing)
        {
            if (isDisposing)
            {
                using (TimedLock.Lock(factory))
                {
                    factory.Close();
                }
            }
        }

        public void CallService(string methodName, params object[] args)
        public void CallService(Action<T> handler)
        {
            CallServiceInternal(methodName, args);
            CallServiceInternal(handler);
        }

        public TReturnType CallService<TReturnType>(string methodName, params object[] args)
        public object CallService(Func<T,object> handler)
        {
            return (TReturnType)CallServiceInternal(methodName, args);
            return CallServiceInternal(handler);
        }

        internal object CallServiceInternal(Delegate handler)
        {
            object returnObj = null;

            try
            {
                EnsureChannelCreated();
                try
                {
                    if (factory != null)
                    {
                        using (TimedLock.Lock(factory))
                        {
                            MethodInfo methodInfo = typeof(T).GetMethod(methodName);
                            returnObj = methodInfo.Invoke(channel, args);
                            returnObj = handler.DynamicInvoke(channel);
                        }
                    }
                }
                catch (TargetInvocationException e)
                {
                    throw e.InnerException;
                }
            }
            catch (FaultException)
            {
                throw; // don't want to catch FaultExceptions which are a subclass of CommunicationException
            }
            catch (CommunicationException e)
            {
                throw new CommunicationHelperException(e.Message, e);
            }
            catch (TimeoutException e)
            {
                throw new CommunicationHelperException(e.Message, e);
            }

            return returnObj;
        }
    }
}

No comments yet.

No Comments »

You must be logged in to post a comment.