Giter Club home page Giter Club logo

castle.core.asyncinterceptor's Introduction

Castle.Core.AsyncInterceptor

NuGet Version NuGet Downloads Build status CircleCI build Status codecov Coverage Status Coveralls Coverage Status Latest release

What is AsyncInterceptor?

AsyncInterceptor is an extension to Castle DynamicProxy to simplify the development of interceptors for asynchronous methods.

Why do I want intercept methods?

The whys and wherefores of implementing interceptors is lengthy discussion, and beyond the scope of this introduction. A very common scenario is in the implementation of Aspect-oriented patterns, for which exception handling is useful use case.

An interceptor that catches exceptions and logs them could be implemented quite simply as:

// Intercept() is the single method of IInterceptor.
public void Intercept(IInvocation invocation)
{
    try
    {
        invocation.Proceed();
    }
    catch (Exception ex)
    {
        Log.Error($"Error calling {invocation.Method.Name}.", ex);
        throw;
    }
}

What's not simple about asynchronous method interception?

When implementing IInterceptor the underlying method is invoked like this:

public void Intercept(IInvocation invocation)
{
    // Step 1. Do something prior to invocation.

    invocation.Proceed();

    // Step 2. Do something after invocation.
}

For synchronous methods Proceed() returns only when the underlying method completes, but for asynchronous methods, (those that return Task or Task<TResult>) the Proceed() returns as soon as the underlying method hits an await (or ContinueWith).

Therefore with asynchronous methods Step 2 is executed before the underlying methods logically completes.

How to intercept asynchronous methods without AsyncInterceptor?

To demonstrate how AsyncInterceptor simplifies the interception of asynchronous methods, let's show how to do it without AsyncInterceptor.

Methods that return Task

To intercept methods that return a Task (Note: it must be a Task not Task<TResult>) is not overly complicated.

The invocation provides access to the return value. By checking the type of the return value it is possible to await the completion of the Task.

public void Intercept(IInvocation invocation)
{
    // Step 1. Do something prior to invocation.

    invocation.Proceed();
    Type type = invocation.ReturnValue?.GetType();
    if (type != null && type == typeof(Task))
    {
        // Given the method returns a Task, wait for it to complete before performing Step 2
        Func<Task> continuation = async () =>
        {
            await (Task) invocation.ReturnValue;

            // Step 2. Do something after invocation.
        };

        invocation.ReturnValue = continuation();
        return;
    }

    // Assume the method is synchronous.

    // Step 2. Do something after invocation.
}

Methods that return Task<TResult>

To intercept methods that return a Task<TResult> is far from simple. It's the reason why I created this library. Rather than go into the detail (the solution requires the use of reflection) the stack overflow question Intercept async method that returns generic Task<> via DynamicProxy provides great overview.

How to intercept asynchronous methods with AsyncInterceptor?

If you've got this far, then it's probably safe to assume you want to intercept asynchronous methods, and the options for doing it manually look like a lot of work.

Option 1: Implement IAsyncInterceptor interface to intercept invocations

Create a class that implements IAsyncInterceptor, then register it for interception in the same way as IInterceptor using the ProxyGenerator extension methods, e.g.

var myClass = new ClasThatImplementsIMyInterface();
var generator = new ProxyGenerator();
var interceptor = new ClasThatImplementsIAsyncInterceptor();
IMyInterface proxy = generator.CreateInterfaceProxyWithTargetInterface<IMyInterface>(myClass, interceptor)

Implementing IAsyncInterceptor is the closest to traditional interception when implementing IInterceptor

Instead of a single void Intercept(IInvocation invocation) method to implement, there are three:

void InterceptSynchronous(IInvocation invocation);
void InterceptAsynchronous(IInvocation invocation);
void InterceptAsynchronous<TResult>(IInvocation invocation);

InterceptSynchronous(IInvocation invocation)

InterceptSynchronous is effectively the same as IInterceptor.Intercept, though it is only called for synchronous methods, e.g. methods that do not return Task or Task<TResult>.

Implementing InterceptSynchronous could look something like this:

public void InterceptSynchronous(IInvocation invocation)
{
    // Step 1. Do something prior to invocation.

    invocation.Proceed();

    // Step 2. Do something after invocation.
}

InterceptAsynchronous(IInvocation invocation)

InterceptAsynchronous(IInvocation invocation) is called for methods that return Task but not the generic Task<TResult>.

Implementing InterceptAsynchronous(IInvocation invocation) could look something like this:

public void InterceptAsynchronous(IInvocation invocation)
{
    invocation.ReturnValue = InternalInterceptAsynchronous(invocation);
}

private async Task InternalInterceptAsynchronous(IInvocation invocation)
{
    // Step 1. Do something prior to invocation.

    invocation.Proceed();
    var task = (Task)invocation.ReturnValue;
    await task;

    // Step 2. Do something after invocation.
}

InterceptAsynchronous<TResult>(IInvocation invocation)

InterceptAsynchronous<TResult>(IInvocation invocation) is called for methods that return the generic Task<TResult>.

Implementing InterceptAsynchronous<TResult>(IInvocation invocation) could look something like this:

public void InterceptAsynchronous<TResult>(IInvocation invocation)
{
    invocation.ReturnValue = InternalInterceptAsynchronous<TResult>(invocation);
}

private async Task<TResult> InternalInterceptAsynchronous<TResult>(IInvocation invocation)
{
    // Step 1. Do something prior to invocation.

    invocation.Proceed();
    var task = (Task<TResult>)invocation.ReturnValue;
    TResult result = await task;

    // Step 2. Do something after invocation.

    return result;
}

Option 2: Extend AsyncInterceptorBase class to intercept invocations

⚠️ PROCEED WITH CAUTION ⚠️ This option come with a major caveat.

If you need to perform asynchronous operations before calling proceed, then you should implement option 1, otherwise you risk thread starvation and deadlocking.

Create a class that extends the abstract base class AsyncInterceptorBase, then register it for interception in the same way as IInterceptor using the ProxyGenerator extension methods, e.g.

var myClass = new ClasThatImplementsIMyInterface();
var generator = new ProxyGenerator();
var interceptor = new ClasThatExtendsAsyncInterceptorBase();
IMyInterface proxy = generator.CreateInterfaceProxyWithTargetInterface<IMyInterface>(myClass, interceptor)

Extending AsyncInterceptorBase provides a simple mechanism to intercept methods using the async/await pattern. There are two abstract methods that must be implemented.

Task InterceptAsync(IInvocation invocation, Func<IInvocation, Task> proceed);
Task<T> InterceptAsync<T>(IInvocation invocation, Func<IInvocation, Task<T>> proceed);

Each method takes two parameters. The IInvocation provided by DaynamicProxy and a proceed function to execute the invocation returning an awaitable task.

The first method in called when intercepting void methods or methods that return Task. The second method is called when intercepting any method that returns a value, including Task<TResult>.

A possible extension of AsyncInterceptorBase for exception handling could be implemented as follows:

public class ExceptionHandlingInterceptor : AsyncInterceptorBase
{
    protected override async Task InterceptAsync(IInvocation invocation, Func<IInvocation, Task> proceed)
    {
        try
        {
            // Cannot simply return the the task, as any exceptions would not be caught below.
            await proceed(invocation).ConfigureAwait(false);
        }
        catch (Exception ex)
        {
            Log.Error($"Error calling {invocation.Method.Name}.", ex);
            throw;
        }
    }

    protected override async Task<T> InterceptAsync<T>(IInvocation invocation, Func<IInvocation, Task<T>> proceed)
    {
        try
        {
            // Cannot simply return the the task, as any exceptions would not be caught below.
            return await proceed(invocation).ConfigureAwait(false);
        }
        catch (Exception ex)
        {
            Log.Error($"Error calling {invocation.Method.Name}.", ex);
            throw;
        }
    }
}

Option 3: Extend ProcessingAsyncInterceptor<TState> class to intercept invocations

Create a class that extends the abstract base class ProcessingAsyncInterceptor<TState>, then register it for interception in the same was as IInterceptor using the ProxyGenerator extension methods, e.g.

var myClass = new ClasThatImplementsIMyInterface();
var generator = new ProxyGenerator();
var interceptor = new ClasThatExtendsProcessingAsyncInterceptor();
IMyInterface proxy = generator.CreateInterfaceProxyWithTargetInterface<IMyInterface>(myClass, interceptor)

Extending ProcessingAsyncInterceptor<TState>, provides a simplified mechanism of intercepting method invocations without having to implement the three methods of IAsyncInterceptor.

ProcessingAsyncInterceptor<TState> defines two virtual methods, one that is invoked before to the method invocation, the second after.

protected virtual TState StartingInvocation(IInvocation invocation);
protected virtual void CompletedInvocation(IInvocation invocation, TState state);

State can be maintained between the two method through the generic class parameter TState. StartingInvocation is called before method invocation. The return value of type TState is then passed to the CompletedInvocation which is called after method invocation.

A possible extension of ProcessingAsyncInterceptor<TState> could be as follows:

public class MyProcessingAsyncInterceptor : ProcessingAsyncInterceptor<string>
{
    protected override string StartingInvocation(IInvocation invocation)
    {
        return $"{invocation.Method.Name}:StartingInvocation:{DateTime.UtcNow:O}";
    }

    protected override void CompletedInvocation(IInvocation invocation, string state)
    {
        Trace.WriteLine(state);
        Trace.WriteLine($"{invocation.Method.Name}:CompletedInvocation:{DateTime.UtcNow:O}");
    }
}

The state of type TState returned from StartingInvocation can be null. Neither StartingInvocation nor CompletedInvocation are require to be overridden in the class that derives from ProcessingAsyncInterceptor<TState>. The default implementation of StartingInvocation simply returns null. If all you require is to intercept methods after they are invoked, then just implement CompletedInvocation and ignore the state parameter which will be null. In that situation your class can be defined as:

public class MyProcessingAsyncInterceptor : ProcessingAsyncInterceptor<object>
{
    protected override void CompletedInvocation(IInvocation invocation, object state)
    {
        Trace.WriteLine($"{invocation.Method.Name}:CompletedInvocation:{DateTime.UtcNow:O}");
    }
}

Using AsyncInterceptor with non-overloaded ProxyGenerator methods

While AsyncInterceptor offers convenient overloads for the most common ProxyGenerator methods, some methods do not (yet) have such overloads. In this case, the extension method IAsyncInterceptor.ToInterceptor() can be used to obtain a regular IInterceptor implementation.

var generator = new ProxyGenerator();
var interceptor = new MyInterceptorWithoutTarget<T>();
generator.CreateInterfaceProxyWithoutTarget(typeof(T), interceptor.ToInterceptor());

Method invocation timing using AsyncTimingInterceptor

A common use-case for method invocation interception is to time how long a method takes to execute. For this reason AsyncTimingInterceptor is provided.

AsyncTimingInterceptor is a specialised implementation of ProcessingAsyncInterceptor<TState> that uses a Stopwatch as the TState.

AsyncTimingInterceptor defines two abstract methods, one that is invoked before method invocation and before the Stopwatch has started. The second after method invocation and the Stopwatch has stopped

protected abstract void StartingTiming(IInvocation invocation);
protected abstract void CompletedTiming(IInvocation invocation, Stopwatch stopwatch);

A possible extension of AsyncTimingInterceptor could be as follows:

public class TestAsyncTimingInterceptor : AsyncTimingInterceptor
{
    protected override void StartingTiming(IInvocation invocation)
    {
        Trace.WriteLine($"{invocation.Method.Name}:StartingTiming");
    }

    protected override void CompletedTiming(IInvocation invocation, Stopwatch stopwatch)
    {
        Trace.WriteLine($"{invocation.Method.Name}:CompletedTiming:{stopwatch.Elapsed:g}");
    }
}

Testing

This library maintains a high level of code coverage with extensive unit tests.

Running the tests

There are several ways to run the tests, the most convenient is through Visual Studio, via Test Explorer or ReSharper.

The tests can also be executed from the command line like this:

dotnet test test/Castle.Core.AsyncInterceptor.Tests/Castle.Core.AsyncInterceptor.Tests.csproj

On Windows the above command will execute the tests on all 3 runtimes, .NETFramework,Version=v4.7, .NETCoreApp,Version=v1.1, and .NETCoreApp,Version=v2.1.

To run the tests targeting a specific runtime (which may be necessary if you don't have all them all installed) run the following command:

dotnet test -f netcoreapp3.1 test/Castle.Core.AsyncInterceptor.Tests/Castle.Core.AsyncInterceptor.Tests.csproj

A docker compose file is provided to run the tests on a linux container. To execute the tests in a container run the following command:

docker build --target test --progress=plain .

Executing the code coverage tests

Code coverage uses the excellent and free OpenCover.

To execute the tests with code coverage (Windows Only) run the following command:

coverage.cmd

Code coverage reports are produced using ReportGenerator and can be viewed in the test/TestResults/Report folder.

castle.core.asyncinterceptor's People

Contributors

dependabot-preview[bot] avatar dependabot-support avatar dependabot[bot] avatar frankyboy avatar github-actions[bot] avatar jonorossi avatar jskimming avatar kosmonikos avatar meirkr avatar ndrwrbgs avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

castle.core.asyncinterceptor's Issues

Can I decorate a class with AsyncInterceptor using dependency injection?

I used to register an interceptor with the DI resolver like this:
WindsorContainer _container;
_container.Register(Component.For()
.ImplementedBy()
.Named(nameof(ExceptionInterceptor)));

Then decorate a class with the interceptor like this:
[Interceptor(nameof(ExceptionInterceptor))]
public class MyClassToBeIntercepted
{
// ...
}

Can I use this method for AsyncInterceptor?

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.

Shortcut invocation

Hi

How would I shortcut the invocation of my async method. Lets say I want to return fast if some creteria is fullfilled, how would I do that?

/Søren

Async Interceptor not working for more than one Interceptor

Hi,

Async Interceptor not gets hits for second Interceptor Its only picking first Interceptor,

I passed different named instances of Interceptor and proxy generator but its not working for second interceptor. Second Interceptor not gets hit.

        container.Register(
            Component.For<IAsyncInterceptor>().ImplementedBy<MetricInterceptorAsync>()
            .Named("IRouterEventProcessorInterceptor")
            .DependsOn(Dependency.OnValue("metricName", MetricConstants.RouterEventProcessor))                 
            .LifestyleTransient());
        
        var eventProcessorInterceptor = container.Resolve<IAsyncInterceptor>("IRouterEventProcessorInterceptor");

        container.Register(Component.For<ProxyGenerator>()
            .Named("EventProcessorGenerator")
            .LifestyleTransient());
            
       var eventProcessorGenerator = container.Resolve<ProxyGenerator>("EventProcessorGenerator");

        var eventProcessor = container.Resolve<INotificationRouterEventProcessor>();

        var eventProcessorGeneratorProxy = eventProcessorGenerator.CreateInterfaceProxyWithTargetInterface<INotificationRouterEventProcessor>(eventProcessor, eventProcessorInterceptor);
        container.Register(Component.For<INotificationRouterEventProcessor>()
            .Instance(eventProcessorGeneratorProxy)
            .OverridesExistingRegistration()
            .LifestyleTransient());
               
        container.Register(
            Component.For<IAsyncInterceptor>()
                .ImplementedBy<MetricInterceptorAsync>()
                .Named("IProfileServiceInterceptor")
                .DependsOn(Dependency.OnValue("metricName", MetricConstants.ProfileApiService))
                .LifestyleTransient());

         var profileApiInterceptor = container.Resolve<IAsyncInterceptor>("IProfileServiceInterceptor");

        var profileApiService = container.Resolve<IProfileApiService>();

        container.Register(Component.For<ProxyGenerator>()
            .Named("ProfileApiServiceGenerator")
            .LifestyleTransient());

        var profileApiServiceGenerator = container.Resolve<ProxyGenerator>("ProfileApiServiceGenerator");

        var profileApiServiceProxy = profileApiServiceGenerator.CreateInterfaceProxyWithTargetInterface<IProfileApiService>(profileApiService, profileApiInterceptor);            

        container.Register(Component.For<IProfileApiService>()
            .Instance(profileApiServiceProxy)
            .OverridesExistingRegistration()
            .LifestyleTransient());

Here profileApiServiceProxy, Interceptor not gets hit.

Thanks,
Vinod Patil

How to use with Castle.Core.InterceptorAttribute?

This seems like exactly what I'm looking for.
I've implemented IAsyncInterceptor and now I want to test. But, I'm using InterceptorAttribute on my IoC managed class:

[Interceptor(typeof(MyAsynchronousInterceptor))]
class MyClass
{
}

However, I'm getting the error An interceptor registered for F1307.HmiClient.WCF.ClientConnectionManager doesn't implement the IInterceptor interface

How can I use it with the attribute?

Question - Register IAsyncInterceptor with CastleWindsor as an Interceptor help wanted

I there

I´ve been trying to use your extension to register async interceptors with no success.

I´ve been totalylost on how i can "tell" CastleWindsor to use a component that implements IAsyncInterceptor as a interceptor.

How can I specify that a component that implements ISumService(for example) should be resolved through CreateInterfaceProxyWithTargetInterface ?

I know that this is a little off-topic, but i can´t find an answer for this.

Thanks in advance.

More Admins

Just putting this out there to see if people want to wade to maintain this library.

So far both @ndrwrbgs and @brunoblank have made significant contributions, I was wondering if you guys would like to become admins.

The CI automation it working well, but I still, out of choice, manually push to NuGet. Automated push to NuGet would make shared admin easier. I'm open to ideas on the best way. I know how to push to NuGet from the CI I'm just doing it manually to finely controll the versions and releases in GitHub.

Allow to intercept IAsyncEnumerable

Hi!

this project is used (among others) in abp.io and is used for client side http-proxy creation based on interfaces.

The poblem I am having is that I would like to "stream" data from the server to the client.
I.e. I would like to return an
IAsyncEnumerable<WeatherInfo> GetCurrentWeather(string region)

Unfortunately, Castle.DynamicProxy.AsyncInterceptorBase seems to not recognize the return type IAsyncEnumerable<T> and thus calls ProceedSynchronous[IAsyncEnumerable'1]

image

I am not too proficient in using Castle DynamicProxy, but if you give me some hints I am willing to try to contribute this as PR!

Any thoughts?

Expected Release Date

Hey guys,

I've been reading your efforts and Castle.Core's efforts for making a truly awaitable interceptor (before & after proceed) and want to congratulate you, good job.

However we do have a question, as we really need this changes to be publicly available.
When can we expect this branch to be merged to master and a new NuGet released ?

Best Regards,
Fernando Nunes

Measure/document performance of interception

For uses such as implementing AOP in C#, performance of the library is a major concern. Ideally, it should be possible to use the library in production code (hot path even), but regardless we should tell users what the performance characteristics of the library are (e.g. if every call that is intercepted will hold a thread for the Wait()).

How would I retry a failed method?

I'm calling a remote service that is rate-limited. If you invoke a method lots of times, it ends up throwing a Exception. I would like to be able to retry a call until it finished successfully. The current code I have is this (notice that I only added a do-loop around your original code :)

        public class ExceptionHandlingInterceptor : AsyncInterceptorBase
        {
            protected override async Task InterceptAsync(IInvocation invocation, Func<IInvocation, Task> proceed)
            {
                bool rateLimit;
                do
                {
                    try
                    {
                        // Cannot simply return the the task, as any exceptions would not be caught below.
                        await proceed(invocation).ConfigureAwait(false);
                        rateLimit = false;
                    }
                    catch (FaceAPIException ex)
                    {
                        if (ex.ErrorCode == "RateLimitExceeded")
                        {
                            rateLimit = true;
                        }
                        else
                        {
                            Log.Error($"Error calling {invocation.Method.Name}.", ex);
                            throw;
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.Error($"Error calling {invocation.Method.Name}.", ex);
                        throw;
                    }

                } while (rateLimit);
            }

            protected override async Task<T> InterceptAsync<T>(IInvocation invocation, Func<IInvocation, Task<T>> proceed)
            {
                bool ratelimit = false;
                T retValue = default(T);
                do
                {
                    try
                    {
                        // Cannot simply return the the task, as any exceptions would not be caught below.
                        retValue = await proceed(invocation).ConfigureAwait(false);
                    }
                    catch (FaceAPIException ex)
                    {
                        if (ex.ErrorCode == "RateLimitExceeded")
                        {
                            ratelimit = true;                            
                        }
                        else
                        {
                            Log.Error($"Error calling {invocation.Method.Name}.", ex);
                            throw;
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.Error($"Error calling {invocation.Method.Name}.", ex);
                        throw;
                    }

                } while (ratelimit);

                return retValue;
            }            
        }

Thanks in advance!

Suggestion: utility methods to create interceptors or even intercepted objects

To provide a simple interceptor like logging start/finish of all methods, users could use a utility method (lightweight) instead of being required to create a class (medium weight for a simple concern).

Example (typed from phone, so pseudo code):

IEquatable<string> sample = EqualityComparer<string>.Default;
// sample name stolen from Rx, if/when implementing spend more time naming
var wrapped = DelegateAsyncInterceptor
      .Intercept<IEquatable<string>>(
            target: sample,
            filter: invocation => true, // optional parameter
            beforeInvoke: async () => {}, // Log
            afterInvoke: async () => {}); // Log

Or at least

IEquatable<string> sample = EqualityComparer<string>.Default;
// sample name stolen from Rx, if/when implementing spend more time naming
var wrapped = DelegateAsyncInterceptor
      .Intercept<IEquatable<string>>(
            target: sample,
            interceptor:
                async (invocation, proceed) =>
                {
                     // TODO: logging
                     await proceed(invocation);
                });

README typo

Section: Option 2: Extend AsyncInterceptorBase class to intercept invocations
class ExceptionHandlingInterceptor implementation example contains 2 typos in comments - // Cannot simply return the the task, as any exceptions would not be caught below.

How to register interfaces in batches

I have many interfaces how to register in batches, for example, I want to register all warehouse interfaces.
inherit AsyncInterceptorBase or IAsyncInterceptor ,can not be used .EnableInterfaceInterceptors().InterceptedBy(typeof(TransactionalInterceptor))

var myClass = new ClasThatImplementsIMyInterface();
var generator = new ProxyGenerator();
var interceptor = new ClasThatExtendsAsyncInterceptorBase();
IMyInterface proxy = generator.CreateInterfaceProxyWithTargetInterface<IMyInterface>(myClass, interceptor)

New release to support awaits before proceed?

I'm seeing an error where if I call await before proceed, it fails. This appears to be known and fixed by #27. However, there has been no release since this was put in. Is this going to be released any time soon?

.net core Task.Run(() => task).GetAwaiter().GetResult() is out of date

Hi,
Is it necessary task wrapping for .net core with Task.Run(() => task).GetAwaiter().GetResult(), just call task.GetAwaiter().GetResult() below several dotnet samples ?

AsyncInterceptor causes too much Task "thread starvation" when we write a method that calls other method, example: method 1 -> Task -> inner method 2 -> Task -> inner method 3 -> Task

image

Under high load we see thread starvation, suspension, locking(init new thread) causes performance impact.

https://stackoverflow.com/questions/17284517/is-task-result-the-same-as-getawaiter-getresult/17284612#17284612 is out of date by writer?

https://stackoverflow.com/questions/17284517/is-task-result-the-same-as-getawaiter-getresult/38530225#38530225 is accepted by same writer above link.

GetAwaiter().GetResult() prevents a thrown exception being wrapped in a AggregateException.
RethrowIfFaulted already checks if its aggreagete exception or not?

dotnet/aspnetcore@6965a66

dotnet/aspnetcore@360b80e

dotnet/aspnetcore@96c1668

dotnet/runtime@d7a062e

 private static void InterceptSynchronousVoid(AsyncInterceptorBase me, IInvocation invocation)
    {
        Task task = me.InterceptAsync(invocation, invocation.CaptureProceedInfo(), ProceedSynchronous);

        // If the intercept task has yet to complete, wait for it.
        if (!task.IsCompleted)
        {
            // Need to use Task.Run() to prevent deadlock in .NET Framework ASP.NET requests.
            // GetAwaiter().GetResult() prevents a thrown exception being wrapped in a AggregateException.
            // See https://stackoverflow.com/a/17284612
            Task.Run(() => task).GetAwaiter().GetResult();
        }

        task.RethrowIfFaulted();
    }

IMethodInterceptor:BeforeInvoke async/await method happen after decorated method

Castle.Core(4.4.0)
Castle.Core.AsyncInterceptor (1.7.0)

public class ScheduleEventInterceptor : IMethodInterceptor
{
public async void BeforeInvoke(InvocationContext invocationContext)
{
//Expect this call complete before decorated method
await [some_async_call].ConfigureAwait(false);
}
}

Expected: some_async_call execute before decorated method

Actual: some_async_call execute after decorated method.

Security vulnerabilities in Castle.Core.AsyncInterceptor

Castle.Core.AsyncInterceptor contains transitive vulnerable packages
image

CVE-2018-8292 / System.Net.Http
CVE-2019-0980 / System.Text.RegularExpressions

The errors can be found using
dotnet list package --vulnerable --include-transitive

Which exact package contains the vulnerability is trial and error unfortunately

From my own experience so far:

xunit 2.4.2 -> xunit 2.4.0 (https://devscope.io/code/xunit/xunit/issues/2568)

Microsoft.PowerShell.SDK
Microsoft.VisualStudio.Web.CodeGeneration.Design
Microsoft.AspNetCore.Authentication

Serilog.Sinks.MSSqlServer (serilog-mssql/serilog-sinks-mssqlserver#417)

AutoFixture
AutoFixture.AutoMoq
AutoFixture.Xunit2
AutoFixture/AutoFixture#1356

PuppeteerSharp
hardkoded/puppeteer-sharp#2014

System.ServiceModel.Http
System.ServiceModel.Security
System.ServiceModel.Duplex
System.ServiceModel.NetTcp

Microsoft.AspNetCore.WebUtilities

Thanks in advance!

Discussion: Directions to take async interception

We've had several discussions on issues and pull requests on this repo, and on Castle.Core, and I wanted to bring it together (if possible).

Firstly, I want to thank you guys for getting involved. I imagine we've all got day jobs, so any time we can give to this is fantastic.

Here is my current thinking, it's by no means an exhaustive list, it's just what's top of mind at the moment.

  1. The design of AsyncInterceptorBase is a mistake.
    • In attempting to provide just two methods to override, both of which are asynchronous, it has made synchronous interception problematic with the potential of causing deadlocks when transitioning back from the asynchronous interceptor to the underlying synchronous method being intercepted.
    • Here's my comment on PR #54 going into a little more detail.
    • I, therefore, agree with @brunoblank in his comment here on PR #40, specifically "I think the AsyncInterceptorBase should be deleted. But, it brings nice abstract methods."
  2. What to do with return value?
    • I think one of the challenges we've had with async before Proceed has been to do with the return value being out of band and as a result out of our control.
    • I like the idea raised by @brunoblank in PR #40, and I wonder what other's think.
    • I believe you @stakx think differently, though I can't remember the comment that gave me that impression.
  3. Closely align with the design of Castle.Core
    • This is related to the return value discussion since the design of the IInvocaton has the return value as a property.
    • I think it is a benefit to match the design of Castle.Core as it adheres to the Principle of least astonishment and developers familiar with Castle.Core will find it easier to pick up AsyncInterceptor.
    • Close alignment to Castle.Core may improve the chances of this becoming incorporated into Castle.Core. This is something I considered when I first started this library, and I've received comments along that line on other issues. Thoughts @stakx?

The current alpha introduces some breaking changes, and I believe we must introduce more, given point 1. I think now is an excellent time to tackle some other gnarly issues if you guys are happy to get involved?

All views regretfully received.

The reflected return type of a method is never null

Having just migrated to codecov.io an untested code path has been found.

The code is checking for a null return type to determine whether a method returns void:

// If there's no return type, or it's not a task, then assume it's a synchronous method.
if (returnType == null || !typeof(Task).IsAssignableFrom(returnType))
	return MethodType.Synchronous;

After a little investigation and from an SO answer, I've found it should check for typeof(void) instead:

// If there's no return type, or it's not a task, then assume it's a synchronous method.
if (returnType == typeof(void) || !typeof(Task).IsAssignableFrom(returnType))
    return MethodType.Synchronous;

SignalWhenComplete<TResult> Completes invocation without setting the result

When using AsyncTimingInterceptor I noticed that the return value in my IInvocation object is the task that is created by InterceptAsynchronous instead of the returned value of the function.
What I think happens is that SignalWhenComplete calls CompletedInvocation before the
invocation.ReturnValue = SignalWhenComplete(invocation, state);
line is executed, leaving the returned value an unstarted task.
I fixed it locally by setting
invocation.ReturnValue = result;
just before calling CompletedInvocation in SignalWhenComplete.

"proceed.Invoke(invocation)" calls again "AsyncInterceptorBase.InterceptAsync"

I am using IAsyncInterceptor with the built-in .Net Core (3.1) IServiceCollection to inject transactional behavior around my services and I am facing a very strange issue:

For some services, but not all of them (despite they are all registered the same way), in my implementation of AsyncInterceptorBase.InterceptAsync, the call to proceed.Invoke(invocation); internally recall InterceptAsync, as you can see on this debugger screenshot:

image

Here is the associated call stack:

image

Here is how I register my services:

public static IServiceCollection AddProxy<TInterface, TImplementation>(this IServiceCollection services, ServiceLifetime lifetime)
{
    // Register the service implementation
    services.Add(new ServiceDescriptor(typeof(TImplementation), typeof(TImplementation), lifetime));
    
    // Register the mapping interface <-> implementation
    services.Add(new ServiceDescriptor(typeof(TInterface), serviceProvider =>
    {
    	// Get a new interceptor instance
    	var interceptor = serviceProvider.GetRequiredService<EndonextAsyncInterceptor>();
    
    	var hasTransactionAttributes = typeof(TImplementation)
    		.GetMethods()
    		.SelectMany(m => m.GetCustomAttributes(typeof(TransactionAttribute), false))
    		.Any();
    
    	// Inject the DbContext if necessary
    	if (hasTransactionAttributes)
    	{
    	    interceptor.DbContext = serviceProvider.GetRequiredService<EndonextDbContext>();
    	}
    
    	// Get the proxy generator and the service implementation instances
    	var proxyGenerator = serviceProvider.GetRequiredService<ProxyGenerator>();
    	var actual = serviceProvider.GetRequiredService<TImplementation>();
    
    	// Return the proxy
    	return proxyGenerator.CreateInterfaceProxyWithTarget(typeof(TInterface), actual, interceptor);
    }, lifetime));
    
    return services;
}

IServiceCollection services;
services
    .AddTransient<AsyncInterceptor>()
    .AddSingleton<ProxyGenerator>()
    .AddWithProxy<IService, Service>(ServiceLifetime.Scoped);

Here is the way I get my service instance:

using (var scope = serviceProvider.CreateScope())
{
    var service = scope.ServiceProvider.GetRequiredService<IService>();

    await service.MyCallAsync();
}

And here is my simplified implementation of the IAsyncInterceptor:

public class AsyncInterceptor : AsyncInterceptorBase
{
    private readonly ILogger<AsyncInterceptor> _logger;
    
    public AsyncInterceptor(ILogger<AsyncInterceptor> logger)
    {
        _logger = logger;
    }
    
    protected override async Task InterceptAsync(IInvocation invocation, Func<IInvocation, Task> proceed)
    {
        LogEntry(invocation);
        
        bool inError = false;
        
        try
        {
            await this.BeginTransactionAsync(invocation);
            
            await proceed.Invoke(invocation);
        }
        catch (Exception)
        {
            inError = true;
            throw;
        }
        finally
        {
            await this.CommitTransactionAsync(invocation, inError);
        }
        
        LogExit(invocation);
    }
}

I only use IInvocation in the BeginTransaction and CommitTransaction for reflection purpose (getting an attribute associated to the invoked method).

I did not found any obvious difference between my services declaration, implementation nor, registration.

Do you have an idea of what is going on?

AsyncInterceptor with Polly (AOP retry style)

For some part of my system I need to add retry logic for reading from the database. I have a number of repositories with async and sync read methods that I can't change. I found a simple solution - interception of all read methods with AsyncInterceptor and add retry read policy with Polly when database exception caught. Polly retries reading with some intervals.

Interceptor code:

public class RetriableReadAsyncInterceptor : IAsyncInterceptor
{
    public void InterceptSynchronous(IInvocation invocation)
    {
        invocation.ReturnValue = InternalInterceptSync(invocation);
    }

    public void InterceptAsynchronous(IInvocation invocation)
    {
        throw new NotImplementedException();
    }

    public void InterceptAsynchronous<TResult>(IInvocation invocation)
    {
        invocation.ReturnValue = InternalInterceptAsync<TResult>(invocation);
    }

    private IEnumerable<TimeSpan> RetryIntervals =>
        new[]
        {
            TimeSpan.FromSeconds(1),
            TimeSpan.FromSeconds(5),
            TimeSpan.FromSeconds(10),
            TimeSpan.FromSeconds(15)
        };

    private object InternalInterceptSync(IInvocation invocation)
    {
        return Policy
            .Handle<DatabaseException>()
            .WaitAndRetry(RetryIntervals, (exception, timeSpan) =>
            {
                Console.WriteLine($"Exception {timeSpan}");
            })
            .Execute(() =>
            {
                invocation.Proceed();
                return invocation.ReturnValue;
            });
    }

    private async Task<TResult> InternalInterceptAsync<TResult>(IInvocation invocation)
    {
        return await Policy
            .Handle<DatabaseException>()
            .WaitAndRetryAsync(RetryIntervals, (exception, timeSpan) =>
            {
                Console.WriteLine($"Exception {timeSpan}");
            })
            .ExecuteAsync(async () =>
            {
                invocation.Proceed();
                var task = (Task<TResult>)invocation.ReturnValue;
                return await task;
            });
    }
}

Repository code:

public class Repository : IRepository
{
    private int _exceptionsCoutner;

    public Entity GetById(int id)
    {
        if (_exceptionsCoutner <= 2)
        {
            _exceptionsCoutner++;
            throw new DatabaseException();
        }

        //read from db
        return new Entity {Id = id};
    }

    public async Task<Entity> GetByIdAsync(int id)
    {
        if (_exceptionsCoutner <= 2)
        {
            _exceptionsCoutner++;
            throw new DatabaseException();
        }

        //read from db
        return await Task.FromResult(new Entity { Id = id });
    }
}

Sync version of GetById works as expected (retries with intervals):

Exception 00:00:01
Exception 00:00:05
Exception 00:00:10

Async version of GetById retries but not waits for time interval elapsed:

Exception 00:00:01
Exception 00:00:01
Exception 00:00:01

I can't understand where is the problem. If you have any thoughts - please share.
Full example can be found here.

Register IAsyncInterceptor As Interceptor in Windsor Container

I created LoggingInterceptor which implements IAsyncInterceptor. Normally I can register my LoggingInterceptor when it implements IInterceptor.
windsorContainer .Register(Component.For<LoggingInterceptor>().LifestyleSingleton()) .Register(Classes.FromAssembly(typeof(IApplyService).Assembly) .Where(c => c.Namespace != null && typeof(IService).IsAssignableFrom(c)) .WithServiceAllInterfaces() .Configure(x => x.Interceptors<LoggingInterceptor>()) .Configure(x => x.Named(x.Implementation.FullName)) .LifestyleTransient());

But when I implement IAsyncInterceptor , it says LoggingInterceptor must be convertible Castle.DinamicProxy.IInterceptor.

How can i overcome this situation ? I am not using AutoFac as DI .

My AsyncInterceptor which injects an Async Logger is intercepting the Logger requests causing a loop

Hi,

I've managed to Implement your IAsyncInterceptor which works great with async method calls. What I'm having problems with now is in my implementation of IAsyncInterceptor, I have injected an IMyAsyncLogger which as the name suggests logs to somewhere asynchronously. I use this IMyAsyncLogger before and after the invocation. However, it seems like when calling the IMyAsyncLogger.LogAsync(...), the IAsyncInterceptor implementation seems to be intercepting these calls even though I've not registered it anywhere. Why is it intercepting the logger calls. Here's my code sample:

Main.cs

public static void Main(string[] args)
        {
            RunAsync().GetAwaiter().GetResult();
           
            Console.ReadLine();
        }

public static async Task RunAsync()
        {
            var service = new MyExternalService();

            var proxyGenerator = new ProxyGenerator();
            var interceptor = new MyAsyncInterceptor(new MyAsyncLogger());
            var proxiedExternalService = proxyGenerator.CreateInterfaceProxyWithTarget<IMyExternalService>(service, interceptor);

            await proxiedExternalService.DoStuffAsync().ConfigureAwait(false);

            Console.WriteLine("-----------------");

            var value  = await proxiedExternalService.GetValueAsync().ConfigureAwait(false);

            Console.WriteLine($"My External Service is {value}");
        }

MyAsyncInterceptor.cs

public class MyAsyncInterceptor : IAsyncInterceptor
    {
        private readonly IMyAsyncLogger _logger;

        public MyAsyncInterceptor(IMyAsyncLogger logger)
        {
            _logger = logger;
        }
        public void InterceptSynchronous(IInvocation invocation)
        {
            invocation.Proceed();
        }

        public void InterceptAsynchronous(IInvocation invocation)
        {
            invocation.ReturnValue = InterceptAsync(invocation);
        }

        public void InterceptAsynchronous<TResult>(IInvocation invocation)
        {
            invocation.ReturnValue = InterceptAsync<TResult>(invocation);
        }

        private async Task InterceptAsync(IInvocation invocation)
        {
            await _logger.LogAsync($"Before Task MyAsyncInterceptor execution").ConfigureAwait(false);
            //Console.WriteLine("Before Task MyAsyncInterceptor execution");

            invocation.Proceed();

            var task = (Task) invocation.ReturnValue;

            await task;

            //Console.WriteLine("After Task MyAsyncInterceptor execution");
            await _logger.LogAsync($"After Task MyAsyncInterceptor execution").ConfigureAwait(false);
        }

        private async Task<TResult> InterceptAsync<TResult>(IInvocation invocation)
        {
            //await _logger.LogAsync($"Before Task<TResult> MyAsyncInterceptor execution").ConfigureAwait(false);
            Console.WriteLine("Before Task<TResult> MyAsyncInterceptor execution");

            invocation.Proceed();
            var task = (Task<TResult>) invocation.ReturnValue;
            TResult result = await task;

            //await _logger.LogAsync($"After Task<TResult> MyAsyncInterceptor execution").ConfigureAwait(false);
            Console.WriteLine("After Task<TResult> MyAsyncInterceptor execution");

            return result;
        }
    }

MyAsyncLogger.cs

public class MyAsyncLogger : IMyAsyncLogger
 {
     public async Task LogAsync(string message)
     {
         await Task.Delay(2000);
         Console.WriteLine(message);
     }
 }

Breaking Change: Simplified interceptor implementation

Hi,

Great work!

I have maintained my own class library for this and I believe the IAsyncInterceptor could be changed a bit to make using async / await less error prone.

Today (the helper function is needed and setting the ReturnValue):

public void InterceptAsynchronous(IInvocation invocation)
{
    invocation.ReturnValue = InternalInterceptAsynchronous(invocation);
}

private async Task InternalInterceptAsynchronous(IInvocation invocation)
{
    // Step 1. Do something prior to invocation.

    invocation.Proceed();
    var task = (Task)invocation.ReturnValue;
    await task;

    // Step 2. Do something after invocation.
}

This could be hidden in the library by changing the IAsyncInterceptor to:

    public interface IAsyncInterceptor
    {
        void InterceptAction(IActionInvocation invocation);

        Task InterceptAsyncAction(IAsyncActionInvocation invocation);

        Task<T> InterceptAsyncFunction<T>(IAsyncFunctionInvocation<T> invocation);

        T InterceptFunction<T>(IFunctionInvocation<T> invocation);
    }

Where the IInvocation interface's Proceed method matches the ReturnType

    public interface IAsyncFunctionInvocation<T> : IInvocationEx
    {
        Task<T> Proceed();
    }

    public interface IAsyncActionInvocation : IInvocationEx
    {
        Task Proceed();
    }
   ... etc ...

Now when implementing an interceptor, you would be able to use async / await directly.
Example:

    internal class MyAsyncInterceptor : IAsyncInterceptor
    {
        public void InterceptAction(IActionInvocation invocation)
        {
            invocation.Proceed();
        }

        public async Task InterceptAsyncAction(IAsyncActionInvocation invocation)
        {
            await Task.Delay(500);
            await invocation.Proceed();
            await Task.Delay(500);
        }

        public async Task<T> InterceptAsyncFunction<T>(IAsyncFunctionInvocation<T> invocation)
        {
            await Task.Delay(500);
            var rv = await invocation.Proceed();
            Console.WriteLine($"rv: {rv}");
            await Task.Delay(500);
            return rv;
        }

        public T InterceptFunction<T>(IFunctionInvocation<T> invocation)
        {
            return invocation.Proceed();
        }
    }

Is this something you would consider?

Thanks
/Bruno

Castle Windsor Registering Interceptors

Hi

I Have Problem On Registering Castle.Core.AsyncInterceptor Interceptor To Castle Wire-up,Castle Need Interceptor From IInterceptor interface and Can Not Implicit Cast IAsynInterceptor To IInterceptor ,How CAn Register IAsynInterceptor in castle Windsor in Code :

Classes.FromAssemblyContaining<FacadeService>()
.BasedOn<IFacadeService>()
.WithService.FromInterface()
.Configure(a => a.Interceptors<ExceptionInterceptor>())
.LifestyleScoped()

Method InterceptAsync of AsyncInterceptorBase invokes many times

I created an interceptor

public class RunSettingsAsyncInterceptor : AsyncInterceptorBase
{
    private readonly IRunSettingsService runSettingsService;
    public RunSettingsAsyncInterceptor(IRunSettingsService runSettingsService)
    {
        this.runSettingsService = runSettingsService;
    }

    protected override async Task InterceptAsync(IInvocation invocation, Func<IInvocation, Task> proceed)
    {
        await CheckSettingEnabled(invocation);

        await proceed(invocation);
    }

    protected override async Task<TResult> InterceptAsync<TResult>(IInvocation invocation, Func<IInvocation, Task<TResult>> proceed)
    {
        await CheckSettingEnabled(invocation);

        return await proceed(invocation);
    }

    private async Task CheckSettingEnabled(IInvocation invocation)
    {
        //request to database
    }
}

Interceptor and service registered to ASP.NET DI Container

services.AddTransient<RunSettingsAsyncInterceptor>();
services.AddScoped<IRunSettingsService, RunSettingsService>();

How do I register a service in DI Container

services.AddScoped<VoWRunService>();
services.AddScoped<IVoWRunService>(sp => CreateProxyWithInterceptor<IVoWRunService, VoWRunService, RunSettingsAsyncInterceptor>(sp));          

Extension method to create a proxy

private static TInterface CreateProxyWithInterceptor<TInterface, TImplementation, TInterceptor>(IServiceProvider serviceProvider)
        where TImplementation : TInterface
        where TInterface : class
        where TInterceptor : IAsyncInterceptor 
    {
        var service = serviceProvider.GetRequiredService<TImplementation>();
        var generator = new ProxyGenerator();
        var interceptor = serviceProvider.GetRequiredService<TInterceptor>();
        var proxy = generator.CreateInterfaceProxyWithTargetInterface<TInterface>(service, interceptor);
        return proxy;
    }

Method InterceptAsync invokes many times, from 50 to 400. Every time I receive a new count of invocations.
Could you please advice how to resolve this issue? I use .NET Core 2.1 and ASP.NET Core

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.