2

The code in question is written by robot (CodeSmith) and it is a pain to maintain. It looks somewhat similar to:

public AddressProgramTemplate GetById(System.Int32 _id) {
    try {
        return Service.GetById(_id);
    } catch (FaultException<ErrorInfo> ex) {
        throw new ProxyServerBusinessException(ex.Detail);
    } catch (FaultException) {
        throw new ProxyServerBusinessException(null);
    } catch (EndpointNotFoundException ex) {
        throw new ProxyServerTechnicalException<EndpointNotFoundException>(ex);
    } catch (CommunicationObjectFaultedException ex) {
        throw new ProxyServerTechnicalException<CommunicationObjectFaultedException>(ex);
    } catch (CommunicationException ex) {
        throw new ProxyServerTechnicalException<CommunicationException>(ex);
    } catch (ObjectDisposedException ex) {
        throw new ProxyServerTechnicalException<ObjectDisposedException>(ex);
    } catch (TimeoutException ex) {
        throw new ProxyServerTechnicalException<TimeoutException>(ex);
    }
}

As you can guess, it's a client-side WCF proxy code and all these lines are repeated for every service method there is (and there are many). What's good for robot is a sorrow for me, so I started to refactor it. First of all, the exception logic and handling is delegated to Microsoft Enterprise Library and common code migrated to base class:

public TResult WrapServiceMethod<TResult>(Func<TResult> serviceMethod) {
    TResult result = default(TResult);
    try {
        result = serviceMethod();
    } catch (Exception ex) {
        bool rethrow = ExceptionManager.HandleException(ex, ExceptionPolicyNames.ClientRequestPolicy);
        if (rethrow) throw;
    }
    return result;
}

So far so good, the ugly try/catch pile becomes a neat one-liner:

return WrapServiceMethod<AddressProgramTemplate>(() => Service.GetById(_id));

A little effort and void methods covered as well. The problem comes when service calls use out parameters:

public void GetPeriod(AddressProgram program, out DateTime startDate, out DateTime endDate){
    WrapServiceMethod(() => Service.GetPeriod(program, out startDate, out endDate));
}

Results in "Cannot use ref or out parameter 'endDate' inside an anonymous method, lambda expression, or query expression" and I understand why.

What I would ideally like to have is an ability to define custom operator blocks such as while() or using(), so I could write

wrapexception { ... }

and live happily ever after, but I don't think this trick is possible with .NET. Assuming that rewriting all service methods without out parameters is a last resort, do I have any other options?

Community
  • 1
  • 1
Stanislav Kniazev
  • 5,386
  • 3
  • 35
  • 44

2 Answers2

2

Sounds like you're after an aspect oriented programming library, such as PostSharp.

You can create Exception handlers that are inserted into your IL after compilation based on rules you specify which can do things such as catching exceptions, logging, tracing etc.

The benefit of this is you write the aspect once, and apply it to multiple methods. These methods aren't cluttered with code unrelated to the specific task at hand, as the cross cutting concerns such as exception handling are taken care of by the aspect.

Take a look at the example at http://www.sharpcrafters.com/solutions/monitoring#exception-monitoring which shows how to handle exceptions.

Michael Shimmins
  • 19,961
  • 7
  • 57
  • 90
  • Looks promising - a robot that modifies compiled assembly instead of source code. Should be much easier to maintain indeed. Are there any serious performance drawbacks? – Stanislav Kniazev Dec 16 '10 at 23:55
  • Should point out that not all AOP libraries are post-compilation. PostSharp is, but others don't modify the compiled assembly. Performance shouldn't be impacted unless you write a dodgy aspect. As usual, profile and compare to be sure. Spring.NET uses proxy objects to create method interceptors as their way of implementing AOP. – Michael Shimmins Dec 16 '10 at 23:58
  • I will probably go with PolicyInjection from Microsoft Enterprise Library just because it's Microsoft (easy to sell) and it's in use in our project already (even easier to sell). – Stanislav Kniazev Dec 18 '10 at 22:17
1

An alternative to changing the out signatures (and thus having to change all calling code, which I'm sure you want to avoid) you could do something like this:

public void GetPeriod(AddressProgram program, out DateTime startDate, out DateTime endDate)
{
    var outs = WrapServiceMethod(() => 
        {
            DateTime sd;
            DateTime ed;
            Service.GetPeriod(program, out sd, out ed));
            return new {sd, ed};
        }
    startDate = outs.sd;
    endDate = outs.ed;
}
Peter Lillevold
  • 33,668
  • 7
  • 97
  • 131