Thursday, March 19, 2009 #

Method signature in IOperationInvoker?

As I said in my previous post, I’m currently implementing a web service in WCF.  It has to be consumable by code that’s been written for a .NET 2.0 style web service (in Perl, as it happens), and so I’m using the BasicHttpBinding and Xml serialisation.

One of the reasons I’m using WCF is to be able to wrap all operations (in the WCF sense) in the same error handling code.  The requirements of the client are always to have the following class returned:

public class Result<T> {

public T Data { get; set; }

public int ErrorNumber { get; set; }

public string ErrorDescription { get; set; }

public bool Success { get { return ErrorNumber > 0; } }

}

If everything goes swingingly, Data is non-null, ErrorNumber is 0 and ErrorDescription is empty.

Should the method on the service class throw an unhandled exception however, Data is null, ErrorNumber is non-zero and ErrorDescription describes what went wrong.

Since this has to happen for every method in the service contract, I wrote an attribute deriving from IServiceBehavior whose ApplyDispatchBehavior method adds an instance of a class implementing IOperationBehavior to every operation on every end point exposed by the service:

public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
        {
            foreach (ServiceEndpoint endpoint in serviceDescription.Endpoints)
                foreach (OperationDescription operation in endpoint.Contract.Operations)
                    operation.Behaviors.Add(new ErrorWrappingOperationBehavior());
        }

ErrorWrappingOperationBehavior, in turn, inserts an instance of a class implementing IOperationInvoker into the invocation pipeline for each method.  The following code represents the bits of this class that don’t just pass straight through to the default invoker:

public class ErrorWrappingOperationInvoker : IOperationInvoker
    {
        private IOperationInvoker invoker;

        public ErrorWrappingOperationInvoker(IOperationInvoker invoker)
        {
            if (invoker.GetType().GetProperty("Method") == null)
            {
                throw new InvalidOperationException("The invoker supplied must have a Method property");
            }
            this.invoker = invoker;
        }

        public object Invoke(object instance, object[] inputs, out object[] outputs)
        {
            object result;
            try
            {
                result = invoker.Invoke(instance, inputs, out outputs);
            }
            catch (Exception ex)
            {
                // TODO: Is there a nicer way of getting at the method being invoked? Seems like quite a
                // common requirement when we're hooking the pipeline?
                MethodInfo webMethod = (MethodInfo)invoker.GetType().GetProperty("Method").GetValue(invoker, null);
                // All web methods should return a Result<T>, which implements IResultDescription
                IResultDescription resultDescription = (IResultDescription)Activator.CreateInstance(webMethod.ReturnType);
                resultDescription.ErrorNumber = (int)EnvironmentError.CouldNotConnectToDatabase;
                resultDescription.Description = ex.Message + Environment.NewLine + ex.StackTrace;
                result = resultDescription;

                // Must assign "out" parameter
                outputs = new object[0];
            }
            return result;           
        }

Appalled?  The problem is that I have to new up an instance of the return type for the method being invoked.  The method being invoked, however, isn’t a member of the IOperationInvoker interface.  Rather it is a property on the default invoker, whose type is internal to its assembly.  So I had to resort to some dodgy reflection to get what I want.

This all works, but leaves me feeling slightly uneasy.  Does anyone have any insight into why the method being invoked isn’t exposed?  This may of course be a case of doing something not recommended.  I imagine I should be returning SOAP faults to adhere to SOA principles, but the consumer (which is internal to our company) is not even reading the WSDL for the service, so this level of adherence isn’t necessary.

  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati

Posted On Thursday, March 19, 2009 5:32 PM | Feedback (3)

Copyright © CodeCurve

Design by Bartosz Brzezinski

Design by Phil Haack Based On A Design By Bartosz Brzezinski