开发者

How can I add common functionality around method calls?

开发者 https://www.devze.com 2023-01-30 19:45 出处:网络
The code in question is written by robot (CodeSmith) and it is a pain to maintain. It looks somewhat similar to:

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<AddressPr开发者_运维问答ogramTemplate>(() => 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?


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.


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;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号