/// <summary> /// Invoke the pipeline with the given input. /// </summary> /// <param name="invocation">Input to the method call.</param> /// <param name="throwOnException">Whether to throw the <see cref="IMethodReturn.Exception"/> if it has a value after running /// the behaviors.</param> /// <returns>Return value from the pipeline.</returns> public IMethodReturn Invoke(IMethodInvocation invocation, bool throwOnException = false) { IMethodReturn InvokeTargetOrThrow(IMethodInvocation invocation) => invocation.HasImplementation ? invocation.CreateInvokeReturn() : throw new NotImplementedException(ThisAssembly.Strings.NotImplemented(invocation)); if (Behaviors.Count == 0) { return(InvokeTargetOrThrow(invocation)); } // We convert to array so that the collection of behaviors can potentially // be modified by behaviors themselves for a subsequent pipeline execution. // The current pipeline execution, once started, cannot be modified, though. var behaviors = Behaviors.ToArray(); var index = -1; for (var i = 0; i < behaviors.Length; i++) { if (!invocation.SkipBehaviors.Contains(behaviors[i].GetType()) && behaviors[i].AppliesTo(invocation)) { index = i; break; } } if (index == -1) { return(InvokeTargetOrThrow(invocation)); } ExecuteHandler GetNext() { for (index++; index < behaviors.Length; index++) { if (!invocation.SkipBehaviors.Contains(behaviors[index].GetType()) && behaviors[index].AppliesTo(invocation)) { break; } } return((index < behaviors.Length) ? behaviors[index].Execute : (m, n) => InvokeTargetOrThrow(m)); } var result = behaviors[index].Execute(invocation, (m, n) => GetNext().Invoke(m, n)); if (throwOnException && result.Exception != null) { throw result.Exception; } return(result); }