Упаковка вызовов метода в класс со стандартным try / catch - PullRequest
19 голосов
/ 23 декабря 2011

У меня есть класс, имеющий около 200+ методов, каждый из этих методов выполняет вызов в базу данных или сетевой ресурс.

В идеале я хотел бы обернуть каждый вызов в попытку / перехват, чтобы перехватить любую из распространенных сетевых или SQL-исключений, и дать пользователю возможность повторить попытку (при необходимости).Тем не менее, добавление этого кода к каждому вызову было бы очень трудоемким и раздутым, насколько это касается кода.

Я думал о переносе каждого из вызовов метода в другой метод, создании делегата,и завернуть код делегата в попытку / улов .. как-то так ...

(игнорировать синтаксис ... это просто концептуальный пример)

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 ]

30 голосов
/ 23 декабря 2011

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

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

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

12 голосов
/ 23 декабря 2011

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

Если вы можете использовать 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();
}
0 голосов
/ 23 декабря 2011

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

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

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

...