Обертка вызовов методу в классе со стандартным try/catch

У меня есть класс, который имеет около 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;
}

Может быть, есть более практичный способ сделать это?

Код автоматически генерируется (с помощью инструмента, который я написал). Я могу легко включить что-то вроде выше, но я хочу, чтобы не писать вышеприведенный код для каждого вызова метода.

Кроме того, если я делаю что-то вроде выше, я могу использовать методы времени и вызовы методов журнала и т.д. Это просто кажется неуклюжим (и не сильно типизированным).

Спасибо Рич.

Ответы

Ответ 1

Вы должны просто сделать что-то вроде:

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

альтернативно, вы можете использовать метапрограммирование для написания динамического "декоратора" для лежащих в основе методов "на лету"... но если вы не знакомы с ILGenerator и т.д., вероятно, лучше не делать этого - это довольно сложная тема.

Ответ 2

Я думаю, что ваша основная идея хороша, но есть более простой способ ее реализации (по крайней мере, вы используете .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, но я не пробовал это решение.

Ответ 3

Если вы можете использовать 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();
}

Ответ 4

Я думаю, что одним из возможных решений может быть AOP. Вы можете пометить любой метод, который вы хотите, с атрибутом и ввести код try/catch в компиляцию.

См. образец здесь

Надеюсь, что это поможет.