Question

I've written a simple object called RequestWrapper that contains single method of type:

TResult WrapRequest<TResult>(Func<TResult> action)

It wraps any action with try..catch, error handling, logging, database connection, transaction (commit & rollback), etc.

Currently I use it like this: (example, not production code)

return RequestWrapper.WrapRequest(() =>
{
    Topic entity = GetRepository<Topic>().Find(uid);

    if (entity == null)
        throw new EntityNotFoundException("Topic not found.");

    return new Topic
    {
        Name = entity.Name,
        Posts = entity.Posts.Select(x => new Post
        {
            Body = x.Body,
        }).ToList()
    };
});

I simply wrap around every method of my RESTful web service (using WCF and WebHttpBinding).

My question is: How should I implement behavior that would do the wrapping for me automatically? Is it possible?

Was it helpful?

Solution

You can use a custom IOperationInvoker which wraps the original one does what you need. The code below shows a sample implementation of one, and you can find more information about invokers at http://blogs.msdn.com/b/carlosfigueira/archive/2011/05/17/wcf-extensibility-ioperationinvoker.aspx.

public class StackOverflow_10156890
{
    [ServiceContract]
    public interface ITest
    {
        [WebGet]
        [WrappedOperationBehavior]
        string Echo(string text);
        [WebInvoke(BodyStyle = WebMessageBodyStyle.WrappedRequest)]
        [WrappedOperationBehavior]
        int Divide(int x, int y);
    }
    public class Service : ITest
    {
        public string Echo(string text)
        {
            return text;
        }
        public int Divide(int x, int y)
        {
            return x / y;
        }
    }
    class RequestWrapperOperationInvoker : IOperationInvoker
    {
        IOperationInvoker originalInvoker;

        public RequestWrapperOperationInvoker(IOperationInvoker originalInvoker)
        {
            this.originalInvoker = originalInvoker;
        }

        public object[] AllocateInputs()
        {
            return this.originalInvoker.AllocateInputs();
        }

        public object Invoke(object instance, object[] inputs, out object[] outputs)
        {
            Console.WriteLine("Do initialization, etc. here");
            object result = null;
            try
            {
                result = this.originalInvoker.Invoke(instance, inputs, out outputs);
            }
            catch (Exception e)
            {
                Console.WriteLine("Log exception: {0}: {1}", e.GetType().FullName, e.Message);
                result = null;
                outputs = null;
            }
            finally
            {
                Console.WriteLine("Do finalization, etc. here");
            }

            return result;
        }

        public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
        {
            throw new NotSupportedException("Only synchronous operations supported");
        }

        public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
        {
            throw new NotSupportedException("Only synchronous operations supported");
        }

        public bool IsSynchronous
        {
            get { return true; }
        }
    }
    class WrappedOperationBehaviorAttribute : Attribute, IOperationBehavior
    {
        public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
        {
        }

        public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
        {
            dispatchOperation.Invoker = new RequestWrapperOperationInvoker(dispatchOperation.Invoker);
        }

        public void Validate(OperationDescription operationDescription)
        {
        }
    }
    public static void Test()
    {
        string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
        ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
        var endpoint = host.AddServiceEndpoint(typeof(ITest), new WebHttpBinding(), "");
        endpoint.Behaviors.Add(new WebHttpBehavior());
        host.Open();
        Console.WriteLine("Host opened");

        WebClient c = new WebClient();
        Console.WriteLine(c.DownloadString(baseAddress + "/Echo?text=Hello%20world"));

        c = new WebClient();
        c.Headers[HttpRequestHeader.ContentType] = "application/json";
        Console.WriteLine(c.UploadString(baseAddress + "/Divide", "{\"x\":12,\"y\":0}"));

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top