Castle.Core.AsyncInterceptor
Castle.Core.AsyncInterceptor copied to clipboard
Using AsyncInterceptor with Autofac
I'm using Autofac and would like to use AsyncInterceptor. However, I cannot register a class that implements IAsyncInterceptor because Autofac expects a class that implements IInterceptor.
From the example in the Readme I don't understand how I could use AsyncInterceptor with Autofac. I thought of using a class that implements IInterceptor decorating a class that implements IAsyncInterceptor. Then I would register the class implementing IInterceptor with Autofac which, in turn, would use the class implementing IAsyncInterceptor.
Am not sure how to do it or whether it can be done.
Any help would be appreciated. Thank you.
Hi @blasferatu,
You'll want to look at https://github.com/JSkimming/Castle.Core.AsyncInterceptor/blob/master/src/Castle.Core.AsyncInterceptor/ProxyGeneratorExtensions.cs which performs the bridge of accepting IAsyncInterceptor and passing it off to Castle, which expects IInterceptor.
Please check back if this isn't enough to keep you moving forward and we can try to provide an answer.
(Specifically, I think you'll want to use AsyncDeterminationInterceptor) to bridge the two.
Hi @ndrwrbgs,
I created a class named ExceptionInterceptor that implements IInterceptor which can be used with Autofac interception. This class, in turn, delegates Intercept method calls to the class that implements IAsyncInterceptor. Class ExceptionInterceptorAsync is the one actually doing the interception as I wanted, i.e. processing asynchronous metohds properly.
Finally, I registered everything with Autofac:
builder.RegisterType<ExceptionInterceptor>();
builder.RegisterType<ExceptionInterceptorAsync<().As<IAsyncInterceptor>();
Finally, the registration of a class for interception:
builder.RegisterType<SomeClass<().As<ISomeInterface>()
.EnableClassInterceptors()
.InterceptedBy(typeof(ExceptionInterceptor));
I think I got it right. I attached files with the code. It might be helpful for someone with the same doubts as me.
Thank you very much for your help and guidance.
Hi @blasferatu , @ndrwrbgs
I tried the above-mentioned method to implement an async interceptor. Interceptor has two purposes: to log execution information and to add the result to the cache. Cache might be an external storage, so I want methods accessing cache to be async also.
Implementation behaves strangely, interceptor method is called multiple (I can not guess how many) times.
I created a simple console application. ITestService.Run is the method being intercepted. UniversalInterceptorAsync is the interceptor.
Could you please tell what I am doing wrong? Thanks
Hi @tornike87,
I executed your code several times and the interceptor method was always called only once. Here is an example of the result in the console:
Hello World!
{
"Class": "InterceptorTestConsoleApp.Test.TestService",
"Method": "Run",
"FromCache": false,
"Arguments": [],
"ReturnValue": 380.0,
"Internal": {},
"InternalError": {},
"Exception": null,
"StartTime": "2018-05-30T16:57:09.4363968+01:00",
"EndTime": "2018-05-30T16:57:13.8954541+01:00",
"Flag": 0
}
I think this is the behaviour you want but are getting multiple results like the one above in a single execution.
Method InterceptAsynchronous<TResult>(IInvocation invocation), which in turn calls async Task<TResult> InternalInterceptAsynchronous<TResult>(IInvocation invocation) were both executed only once as expected. As a consequence, method Run in class TestService was executed only once as well.
I didn't find anything wrong with your code. I compared your implementation with mine and both seem right to me. I am sorry I have been unable to reproduce your problem and can't be more helpful.
Best regards.
@blasferatu that is the behaviour I want, but it doesn't work that way always. We have the project where we need to use this interceptor and when we run it on some PC-s it does not finish writing log several minutes.
Can you vlean and debug it with visual studio? When I click F11 on invocation.proceed() it goes back to universal interceptor, makes several loops.
If you can not reproduce it I will try to play with it and make better example of this behaviour.
Thank you very much for your help.
Hello @tornike87,
I cleaned the solution and executed your code repeatedly in Visual Studio 2017 Professional version 15.7.2 with breakpoints in class UniversalInterceptor's Intercept method and class UniversalInterceptorAsync's async Task<TResult> InternalInterceptAsynchronous<TResult>(IInvocation invocation) method.
When the code runs, _asyncInterceptor.ToInterceptor().Intercept(invocation); is executed only once in UniversalInterceptor's Intercept method and then method async Task<TResult> InternalInterceptAsynchronous<TResult>(IInvocation invocation) is executed in class UniversalInterceptorAsync only once also. When the code hits invocation.Proceed();, I press F11 and method Task<double> Run() in class TestService is executed normally. After that, the execution continues as expected and I get the desired result.
I have done this several times during the day and everything has worked correctly. I will keep trying and will let you know if I find anything new about this.
I do hope you can find what's wrong because I've not been successful in trying to help you.
Best regards.
@blasferatu I made a simple change, inserted await Task.Delay(1000); in UniversalInterceptorAsync:158 method GetFromCacheAsync. When I debug or run the application it does not finish processing and log anything, If I move that line to line 171 in the method AddToCacheAsync it logs ExecuteInformation in every second and does not finish processing, I think it just goes to infinite loop. I uploaded source with this change.
Thank you very much for your time and effort!
@tornike87
I have exactly the same behaviour you describe when I run the latest code sample you provided.
When running again your first code sample, I found that
-
If I set a breakpoint in method
async Task<object> GetObjectAsync(string key)in classInMemoryCache(line 94), wait a few seconds (e.g. 10 seconds just to make sure it's a long time) and then press F11 to continue, I get the behaviour you described when you first asked for help. -
I also set a breakpoint in method
async Task<TResult> InternalInterceptAsynchronous<TResult>(IInvocation invocation)in classUniversalInterceptorAsyncininvocation.Proceed();(line 97). With this set up, methodIntercept(IInvocation invocation)in classUniversalInterceptorgets called more than once.
The only difference I see between your code and mine in the interceptor methods (e.g. async Task<TResult> InternalInterceptAsynchronous<TResult>(IInvocation invocation)) is that in those methods I don't have any asynchronous code before invocation.Proceed();. I wonder if this might be related to the problem.
Best regards.
@blasferatu You are absolutely right if I change Async methods when accessing the cache and use synchronous ones, the problem does not happen. I just want methods accessing the cache to be async and I wonder if it is possible. Also for some interceptors, I want to have some logic accessing some web services or other external resources and I want to know if it is possible to call async methods. Could it be related to the autofac registration or is it a general problem with this library?
Thank you very much!
@tornike87,
I am afraid I don't know the answer to your question.
I can only add that in my interception methods I do have asynchronous code after invocation.Proceed();.
I wish someone more knowledgeable than me could provide more insight into this matter.
Best regards.
Hey folks, sorry, I’ve been busy and this was on my TODO list.
Indeed the library does not work today if you have any await before the await proceed, that has to be the first await due to constraints in Castle.
James and I have some discussions going over there (and perhaps in issues here? I’m mobile so cannot link them) regarding this but today you cannot await operations until after you await proceed. On Fri, Jun 1, 2018 at 10:08 AM blasferatu [email protected] wrote:
@tornike87 https://github.com/tornike87,
I am afraid I don't know the answer to your question.
I can only add that in my interception methods I do have asynchronous code after invocation.Proceed();.
I wish someone more knowledgeable than me could provide more insight into this matter.
Best regards.
— You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub https://github.com/JSkimming/Castle.Core.AsyncInterceptor/issues/42#issuecomment-393945933, or mute the thread https://github.com/notifications/unsubscribe-auth/AKRxOoXxvQfGQTiRq-4sZHUZVqhgzT6Eks5t4XTvgaJpZM4T20Oj .
Here are some suggestions I’ve used successfully with this limitation while we push for changes in Castle.
- If your Before action can continue asynchronously and does not return a value used for proceed (eg logging) you can kick it off before await proceed and await it after
- If your code is not production code or is a singleton style setup you can synchronously wait for the Before operation (Castle is wanting a synchronous return so while this would eat a Thread it would permit you run Before to completion and get its result)
@ndrwrbgs, Thank you for the information and suggestions.
@ndrwrbgs, @blasferatu Thank you very much.
@ndrwrbgs Is there any thread regarding this problem in Castle?
Best regards.
Relevant threads: https://github.com/JSkimming/Castle.Core.AsyncInterceptor/issues/25 - Original report https://github.com/JSkimming/Castle.Core.AsyncInterceptor/pull/26 - A PR that tried (but failed) to fix it locally https://github.com/castleproject/Core/issues/145 - A full discussion in the Castle project
Last update was we are really really hoping the Castle experts can make Async first-class support, though I believe they're hoping we can do it :(
see this sample for autofac async interceptor with the help of AsyncDeterminationInterceptor and AsyncInterceptorBase: https://github.com/wswind/Learn-AOP/tree/master/AutofacAsyncInterceptor
- create an adapter
public class AsyncInterceptorAdaper<TAsyncInterceptor> : AsyncDeterminationInterceptor
where TAsyncInterceptor : IAsyncInterceptor
{
public AsyncInterceptorAdaper(TAsyncInterceptor asyncInterceptor)
: base(asyncInterceptor)
{ }
}
- create your async interceptor
public class CallLoggerAsyncInterceptor : AsyncInterceptorBase
{
....
}
- relate the interceptor to interface
[Intercept(typeof(AsyncInterceptorAdaper<CallLoggerAsyncInterceptor>))]
public interface ISomeType
- register to IoC container
//register adapter
builder.RegisterGeneric(typeof(AsyncInterceptorAdaper<>));
//register async interceptor
builder.Register(c => new CallLoggerAsyncInterceptor(Console.Out));
CallLoggerAsyncInterceptor
how to asyncinterceptor for class ?
CallLoggerAsyncInterceptorhow to asyncinterceptor for class ?
here is the demo code: https://github.com/wswind/Learn-AOP/blob/master/AutofacAsyncInterceptor/CallLoggerAsyncInterceptor.cs
Hi @ndrwrbgs,
I created a class named
ExceptionInterceptorthat implementsIInterceptorwhich can be used with Autofac interception. This class, in turn, delegates Intercept method calls to the class that implementsIAsyncInterceptor. ClassExceptionInterceptorAsyncis the one actually doing the interception as I wanted, i.e. processing asynchronous methods properly.Finally, I registered everything with Autofac:
builder.RegisterType<ExceptionInterceptor>(); builder.RegisterType<ExceptionInterceptorAsync<().As<IAsyncInterceptor>();Finally, the registration of a class for interception:
builder.RegisterType<SomeClass<().As<ISomeInterface>() .EnableClassInterceptors() .InterceptedBy(typeof(ExceptionInterceptor));I think I got it right. I attached files with the code. It might be helpful for someone with the same doubts as me.
Thank you very much for your help and guidance.
It should be ok if inherit AsyncInterceptorBase, IInterceptor at the same time. then just simply Intercept as below:
public void Intercept(IInvocation invocation)
{
this.ToInterceptor().Intercept(invocation);
}