0
我試圖通過使用VirtualMethodInterceptor的Unity攔截在我的應用程序中集成瞬態故障處理應用程序塊。統一攔截調用基本方法
我創建了一個調用處理程序來創建攔截方法的動作或func或任務,並將其傳遞到瞬態故障處理程序中,但現在我只是得到一個stackoverflow異常。這是有道理的,因爲故障處理程序將調用統一將攔截並傳遞到故障處理程序的方法。
我需要的是當錯誤處理程序回調或直接調用基本方法時,能夠跳過截取。一些如何能夠做到這一點,因爲它最終會調用我的類方法。
我的攔截碼
public class RetryHandler : ICallHandler
{
// store a cache of the delegates
private static readonly ConcurrentDictionary<MethodInfo, Func<object, object[], object>> CacheCall =
new ConcurrentDictionary<MethodInfo, Func<object, object[], object>>();
// List of methods we need to call
private static readonly Action<Action> RetryActionMethod = RetryPolicy.NoRetry.ExecuteAction;
private static readonly Func<Func<int>, int> RetryFuncMethod = RetryPolicy.NoRetry.ExecuteAction;
private static readonly Func<Func<Task<int>>, Task<int>> RetryAsyncFuncMethod = RetryPolicy.NoRetry.ExecuteAsync;
private static readonly Func<Func<Task>, Task> RetryAsyncActionMethod = RetryPolicy.NoRetry.ExecuteAsync;
private static readonly ConstructorInfo RetryPolicyConstructor;
static RetryHandler()
{
RetryPolicyConstructor =
typeof (RetryPolicy).GetConstructor(new[]
{typeof (ITransientErrorDetectionStrategy), typeof (RetryStrategy)});
}
public int Order { get; set; }
/// <summary>
/// Uses Expression Trees to wrap method call into retryhandler
/// </summary>
/// <param name="input"></param>
/// <param name="getNext"></param>
/// <returns></returns>
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
if (input.Arguments.Count == input.Inputs.Count)
{
var methodInfo = input.MethodBase as MethodInfo;
if (methodInfo != null)
{
var func = CacheCall.GetOrAdd(methodInfo, BuildLambda);
var results = func(input.Target, input.Inputs.OfType<object>().ToArray());
var ex = results as Exception;
if (ex != null)
{
return input.CreateExceptionMethodReturn(ex);
}
return input.CreateMethodReturn(results);
}
}
return getNext()(input, getNext);
}
private static Func<object, object[], object> BuildLambda(MethodInfo methodInfo)
{
var retryAttribute = methodInfo.GetCustomAttributes<RetryAttribute>(true).First();
// Convert parameters and source object to be able to call method
var target = Expression.Parameter(typeof (object), "target");
var parameters = Expression.Parameter(typeof (object[]), "parameters");
Expression source;
if (methodInfo.DeclaringType == null)
{
source = target;
}
else
{
source = Expression.Convert(target, methodInfo.DeclaringType);
}
var convertedParams =
methodInfo.GetParameters()
.Select(
(p, i) =>
Expression.Convert(Expression.ArrayIndex(parameters, Expression.Constant(i)),
p.ParameterType)).Cast<Expression>()
.ToArray();
//!!!!! **This line of code causes the stackoverflow as this will go back through interception**
var innerExpression = Expression.Call(source, methodInfo, convertedParams);
// get what type of lambda we need to build and what method we need to call on the retry handler
Type returnType;
MethodInfo retryMethod;
if (methodInfo.ReturnType == typeof (void))
{
returnType = typeof (Action);
retryMethod = RetryActionMethod.Method;
}
else if (methodInfo.ReturnType == typeof (Task))
{
returnType = typeof (Func<Task>);
retryMethod = RetryAsyncActionMethod.Method;
}
else if (methodInfo.ReturnType.IsGenericType &&
methodInfo.ReturnType.GetGenericTypeDefinition() == typeof (Task<>))
{
var genericType = methodInfo.ReturnType.GetGenericArguments()[0];
returnType =
typeof (Func<>).MakeGenericType(
typeof (Task<>).MakeGenericType(genericType));
retryMethod = RetryAsyncFuncMethod.Method.GetGenericMethodDefinition().MakeGenericMethod(genericType);
}
else
{
returnType = typeof (Func<>).MakeGenericType(methodInfo.ReturnType);
retryMethod =
RetryFuncMethod.Method.GetGenericMethodDefinition().MakeGenericMethod(methodInfo.ReturnType);
}
var innerLambda = Expression.Lambda(returnType, innerExpression);
var outerLambda = Expression.Lambda(
typeof (Func<,,>).MakeGenericType(typeof (object), typeof (object[]), returnType),
innerLambda,
target, parameters);
// create the retry handler
var retryPolicy = Expression.New(RetryPolicyConstructor,
Expression.Invoke(retryAttribute.TransientErrorDetectionStrategy),
Expression.Invoke(retryAttribute.RetryStrategy));
var passedInTarget = Expression.Parameter(typeof (object), "wrapperTarget");
var passedInParameters = Expression.Parameter(typeof (object[]), "wrapperParamters");
var retryCall = Expression.Call(retryPolicy, retryMethod,
Expression.Invoke(outerLambda, passedInTarget, passedInParameters));
Expression resultExpression;
if (methodInfo.ReturnType != typeof (void))
{
// convert to object so we can have a standard func<object, object[], object>
resultExpression = Expression.Convert(retryCall, typeof (object));
}
else
{
// if void we will set the return results as null - it's what unity wants and plus we keep our signature
var returnTarget = Expression.Label(typeof (object));
resultExpression = Expression.Block(retryCall, Expression.Label(returnTarget, Expression.Constant(null)));
}
var func =
Expression.Lambda<Func<object, object[], object>>(resultExpression, passedInTarget, passedInParameters)
.Compile();
return func;
}
}
我的屬性
public abstract class RetryAttribute : HandlerAttribute
{
public RetryAttribute()
{
RetryStrategy =() =>
Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryStrategy.NoRetry;
TransientErrorDetectionStrategy =() => RetryPolicy.NoRetry.ErrorDetectionStrategy;
}
public Expression<Func<RetryStrategy>> RetryStrategy { get; protected set; }
public Expression<Func<ITransientErrorDetectionStrategy>> TransientErrorDetectionStrategy { get; protected set; }
public override ICallHandler CreateHandler(IUnityContainer container)
{
return new RetryHandler();
}
}
public class SqlDatabaseeRetryAttribute : RetryAttribute
{
public SqlDatabaseeRetryAttribute()
{
RetryStrategy =
() => Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryStrategy.DefaultExponential;
TransientErrorDetectionStrategy =() => new SqlDatabaseTransientErrorDetectionStrategy();
}
}