使用标准try / catch包装对类的方法的调用

时间:2011-12-23 15:38:09

标签: c# .net exception delegates

我有一个包含大约200多种方法的类,每种方法都会调用数据库或网络资源。

理想情况下,我想将每个调用包装在try / catch中,以捕获任何常见的网络或SQL异常,并让用户有机会再次尝试(如果适用)。但是,将此代码添加到每个调用中将非常耗时,并且就代码而言也会膨胀。

我已经考虑过在另一个方法中包装每个方法调用,创建一个委托,并将委托代码包装在try / catch中......就像这样......

(忽略语法......这只是一个概念性示例)

bool CallUpdatePassenger(int PassengerId,string PassengerName,string PhoneNumber)
{
    Delegate del= Delegate.CreateDelegate(typeof(UpdatePassengerDelegate), typeof(IPassengerServices).GetMethod("RemoteUpdatePassenger"));
    bool Res=(bool)CallDelegate(del,PassengerName,PhoneNumber);
}
object CallDelegate(Delegate del,params object[] args)
{
    object Result=null;
    try
    {
        Result=del.DynamicInvoke(args);
    }
    catch (Some.Timeout.Error.Or.Whatever te)
    {
        // take some action.. maybe retry etc.. 
    }
    return Result;
}

也许还有一种更实用的方法呢?

代码是自动生成的(通过我编写的工具)我可以很容易地包含类似上面的内容,但我想避免为每个方法调用编写上面的代码。

另外,如果我执行上述操作,我可以计算方法,记录方法调用等。它看起来有点笨拙(并且没有强类型)。

由于 丰富。

4 个答案:

答案 0 :(得分:29)

你应该能够做类似的事情:

T Execute<T>(Func<T> func) {
    try {
        return func();
    } catch (...) {
        ...
    }
}
bool CallUpdatePassenger(some args here) {
    return Execute( () => realObj.RemoteUpdatePassenger(some args here));
}

或者,您可以使用元编程为动态基础方法编写动态“装饰器”...但除非您熟悉ILGenerator等可能最好不要 - 它是相当先进的主题。

答案 1 :(得分:12)

我认为你的基本想法很好,但有一种更简单的方法来实现它(至少你使用.Net 3.5或更新版本):

void WithStandardRetryLogic(Action method) 
{
    try
    {
        method();
    }
    catch (Some.Timeout.Error.Or.Whatever te)     
    {         
        // take some action.. maybe retry etc..      
    } 
}

使用示例:

WithStandardRetryLogic(delegate() { CallUpdatePassenger(PassengerId, PassengerName, PhoneNumber); });

这也可能是AOP框架可能有用的东西,但我还没有尝试过这个解决方案。

答案 2 :(得分:2)

如果您可以使用PostSharp,则可以使用此方面:

[Serializable]
public class RetryAttribute : MethodInterceptionAspect
{
    private readonly int _times;

    public RetryAttribute(int times)
    {
        _times = times;
    }

    public override void OnInvoke(MethodInterceptionArgs args)
    {
        for (var left = _times; left > 0; left--)
        {
            try
            {
                args.Proceed();
                break;
            }
            catch (Exception)
            {
            }
        }
        args.Proceed(); // optional
    }
}

用法如下:

[Retry(2)]
public static void DoIt()
{
    Console.WriteLine("tried");
    throw new Exception();
}

答案 3 :(得分:0)

我认为其中一个可能的解决方案是AOP编程。您可以使用属性标记所需的任何方法,并在编译时注入try/catch代码。

查看示例here

希望这有帮助。