如何在方法调用周围添加常用功能?

时间:2010-12-16 23:25:43

标签: c# lambda dry

有问题的代码是由机器人(CodeSmith)编写的,维护起来很痛苦。它看起来有点类似于:

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

正如您所猜测的那样,它是一个客户端WCF代理代码,所有这些行都会针对每种服务方法重复(并且有很多)。什么对机器人有好处是对我的悲伤,所以我开始重构它。首先,将异常逻辑和处理委托给Microsoft Enterprise Library,并将公共代码迁移到基类:

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

到目前为止,这个丑陋的尝试/捕获堆变成了一个整洁的单线:

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

还包括一些努力和无效方法。服务调用使用out参数时会出现问题:

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

结果“无法在匿名方法,lambda表达式或查询表达式中使用ref或out参数'endDate'”,我理解why

我理想的是能够定义自定义运算符块,例如while()或using(),所以我可以写

wrapexception { ... }

从此过上幸福的生活,但我不认为这个技巧可以用于.NET。假设重写所有没有out参数的服务方法是最后的选择,我还有其他选择吗?

2 个答案:

答案 0 :(得分:2)

听起来像是在使用面向方面的编程库,例如PostSharp。

您可以根据您指定的规则创建在编译后插入IL的异常处理程序,这些规则可以执行诸如捕获异常,记录,跟踪等操作。

这样做的好处是您可以编写一次方面,并将其应用于多种方法。这些方法不会被与手头特定任务无关的代码混乱,因为异常处理等交叉问题由方面处理。

查看http://www.sharpcrafters.com/solutions/monitoring#exception-monitoring上的示例,其中显示了如何处理异常。

答案 1 :(得分:1)

替换out签名(因此必须更改所有调用代码,我确定您要避免),您可以执行以下操作:

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