-
Notifications
You must be signed in to change notification settings - Fork 47
IAsyncInterceptor with return values #40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
IAsyncInterceptor with return values #40
Conversation
…ll be propageted all the way up to the caller.
@JSkimming take your time, I have no intention of stressing this through. All help / thoughts and feedback are welcome! I realized the purpose of
Looking at the code-coverage, Adding a tests for |
|
@brunoblank Thanks for this.
Yes, it would be good to get the coverage back up, and while it was 100% I know it is sometimes unachievable, though the higher the better. |
| /// <param name="invocation">The method invocation.</param> | ||
| public void Intercept(IInvocation invocation) | ||
| { | ||
| try |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally previously (with GetMethodType helper and MethodType enum) it was easier to read this method
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that makes sense. Good call
| } | ||
| else if (typeof(Task).IsAssignableFrom(invocation.Method.ReturnType)) | ||
| { | ||
| invocation.ReturnValue = InterceptAsyncFunctionMethodInfo |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any reason to remove the "concurrent dictionary" implementation based on method delegates?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I was under the assumption that performance cost was Getting the MethodInfo and not calling Invoke. After doing a few benchmarks it was clear that the AsyncDeterminationInterceptor was superior in performance.
Reverted back to original implementation with the addition of Function handling
| } | ||
| else | ||
| { | ||
| invocation.ReturnValue = InterceptFunctionMethodInfo.MakeGenericMethod(invocation.Method.ReturnType) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same issue with reflection
- some info regarding invoking methods performance penalties
https://codeblog.jonskeet.uk/2008/08/09/making-reflection-fly-and-exploring-delegates/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the link! Reverted back to AsyncDeterminationInterceptor
| /// </param> | ||
| protected InvocationBase(IInvocation invocation) | ||
| { | ||
| Invocation = invocation; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't you want to implement the Proceed method as well?
Here I see the mix of IInvocation and IAsyncInvocation methods
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question.
I have intentionally not added Proceed and ReturnValue to IAsyncInvocation as it should not be visible to the Interceptor.
The return-value handling and proceed is implemented separately in each pair of
class FunctionInvocation<TResult> : InvocationBase, IFunctionInvocation<TResult>The InvocationBase class basically wraps IInvocation from Castle.
| /// <see cref = "NotImplementedException" /> will be thrown. | ||
| /// </remarks> | ||
| /// <returns>The asynchronous task</returns> | ||
| Task Proceed(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about naming ProceedAsync?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, I don't like to have the Async suffix.
But it is important to express intent and since the synchronous version is called Proceed is a good idea to change to ProceedAsync to be extra clear.
| /// <see cref = "NotImplementedException" /> will be thrown. | ||
| /// </remarks> | ||
| /// <returns>The return value of the intercepted call</returns> | ||
| Task<TResult> Proceed(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about naming ProceedAsync?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, I don't like to have the Async suffix.
But it is important to express intent and since the synchronous version is called Proceed is a good idea to change to ProceedAsync to be extra clear.
…nous to Action / Function differentiation
|
Thanks @alex-training for good comments. I have updated PR. The |
|
I did a small comparison benchmark comparing Baseline is using the Generic approach directly. The |
|
@alex-training thanks for joining in. It's great to get further input. I'll start weighing in soon. I've never quite known whether this library is receiving much usage, the engagement of others is rather heartening. I'm open to wider administration such that other interested parties can join in with merging pull requests and pushing to NuGet. If people are interested, let's discuss that in another thread. |
|
@brunoblank , @JSkimming |
|
@brunoblank I've just had a look and made a few minor changes. I've restored the intent of the I've added a comment stating the intent of the test, which remains the same, what has changed is the behaviour of async interception. Hence, I've changed the tests to reflect the new behaviour rather the class being intercepted. So far I'm liking the changes 👍 |
|
Nice, I understand the intent of the I am still a bit confused about the The |
|
@brunoblank Thanks for that last commit. I'm afraid my weekend was busier than expected and I've not been able to devote much time to this. I will endeavour to review it soon. |
|
Hi @JSkimming, how are things looking at your end? |
|
@brunoblank rather hectic. We have a new product going live on Tuesday and I've not been able to devote any free time to this in recent weeks. I hope to get some time next weekend. |
|
@JSkimming I have spent some time looking at this feature again. While troubleshooting the problem in core and understand more of what the problem was. I think this PR solves a few of the problems. But I am not happy with it. There are some key benefits here, the key thing is the return-values in Then, after the discussions regarding protected abstract Task<TResult> InterceptAsync<TResult>(
IInvocation invocation,
IInvocationProceedInfo proceedInfo,
Func<IInvocation, IInvocationProceedInfo, Task<TResult>> proceed);My suggestion is to update the public interface IAsyncInterceptor
{
void InterceptAction(IAsyncInvocation invocation, Action proceed);
TResult InterceptFunction<TResult>(IAsyncInvocation invocation, Func<TResult> proceed);
Task InterceptAsyncAction(IAsyncInvocation invocation, Func<Task> proceed);
Task<TResult> InterceptAsyncFunction<TResult>(IAsyncInvocation invocation, Func<Task<TResult>> proceed);
} |
|
@brunoblank I've brought this discussion, along with a couple of other's into issue #60, I'd like to see if a slightly broader discussion can be had first, though that doesn't mean we can't come back to this later |
Make IAsyncInterceptor easier use.
asyncdirectly.awaitcan be used oninvocation.Proceed()as it returns the value includingTasksupport