Динамический повтор прокси
Рассмотрим ниже, что работает:
public interface IService
{
void DoSomething(object arg);
void DoSomethingElse(object arg, bool anotherArg);
bool AndDoYetMoreStuff(object arg, object[] moreArgs);
}
public class Service : IService
{
public void DoSomething(object arg){}
public void DoSomethingElse(object arg, bool anotherArg){}
public bool AndDoYetMoreStuff(object arg, object[] moreArgs)
{
return true;
}
}
public class ServiceRetryProxy : IService
{
const int retryLimit = 3;
private readonly IService _service;
public ServiceRetryProxy(IService service)
{
_service = service;
}
private void RetryOnException(Action<IService> ctx)
{
ReconnectOnException(service =>
{
ctx(service);
return new object();
});
}
private T RetryOnException<T>(Func<IService, T> ctx)
{
var counter = 0;
Exception lastException = null;
while (counter < retryLimit)
{
try
{
return ctx(_service);
}
catch (Exception ex)
{
lastException = ex;
counter++;
}
}
throw lastException;
}
public void DoSomething(object arg)
{
ReconnectOnException(x => x.DoSomething(arg));
}
public void DoSomethingElse(object arg, bool anotherArg)
{
ReconnectOnException(x => x.DoSomethingElse(arg, anotherArg));
}
public bool AndDoYetMoreStuff(object arg, object[] moreArgs)
{
return ReconnectOnException(x => x.AndDoYetMoreStuff(arg, moreArgs));
}
}
Проблема в том, что я должен написать прокси-метод для каждого метода интерфейса. Я хотел бы более "динамическое" решение, чтобы я мог применить RetryOnException (или любую другую логику) для каждого метода в любом данном интерфейсе. В настоящее время я смотрю на Castle DynamicProxy, но что если есть другие варианты?
1 ответ
Замок динамического прокси, безусловно, один из вариантов.
Но я бы лично пошел на postharp: http://www.sharpcrafters.com/
Я уже использовал это для подобного сценария. Я создал RetryOnexceptionAspect, где вы можете указать тип исключения, количество повторных попыток и применить его к любому методу:
[Serializable]
public class RetryOnExceptionAspect : MethodInterceptionAspect
{
private readonly int maxRetries;
private readonly Type exceptionType;
public RetryOnExceptionAspect(int maxRetries, Type exceptionType)
{
this.maxRetries = maxRetries;
this.exceptionType = exceptionType;
}
public override void OnInvoke(MethodInterceptionArgs args)
{
for (int i = 0; i < maxRetries + 1; i++)
{
try
{
args.Proceed();
}
catch (Exception e)
{
if (e.GetType() == exceptionType && i < maxRetries)
{
continue;
}
throw;
}
break;
}
}
}
Использование как:
public class Service : IService
{
[RetryOnExceptionAspect(5, typeof(TimeoutException)]
public void DoSomething()
{
}
}