Обертка вызовов методу в классе со стандартным 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
в компиляцию.
См. образец здесь
Надеюсь, что это поможет.