question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

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

See original GitHub issue

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?

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
ndrwrbgscommented, Dec 19, 2020

Looks related to some of the things you fixed for me so the upgrade fixing it makes sense to me!

On Fri, Dec 18, 2020, 4:05 AM James Skimming notifications@github.com wrote:

Closed #117 https://github.com/JSkimming/Castle.Core.AsyncInterceptor/issues/117.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/JSkimming/Castle.Core.AsyncInterceptor/issues/117#event-4129039071, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACSHCOUEPNYU3DLGIIVFPY3SVMSOFANCNFSM4VAEP2OQ .

1reaction
fharrcommented, Dec 18, 2020

Yes I am using the 1.7.0 version. I migrated to 2.0.21-alpha and it apparently solves the case, no matter the order of execution of my HostedServices. It’s good for me !

Thank you for this great package by the way ! Can’t wait for the 2.0 version 😄.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Intercept the call to an async method using DynamicProxy
Called FooAsync Returned from FooAsync with a task Task from FooAsync completed, ... ReturnValue = InterceptAsync((dynamic)invocation.
Read more >
How would I retry a failed method? · Issue #25
I would like to be able to retry a call until it finished successfully. ... Calls to proceed after the first await are...
Read more >
JSkimming/Castle.Core.AsyncInterceptor
The IInvocation provided by DaynamicProxy and a proceed function to execute the invocation returning an awaitable task. The first method in called when ......
Read more >
Intermittent AbpRemoteCallException Responses when ...
InterceptAsync (IAbpMethodInvocation invocation) at Volo. ... During one process, Service A will call Service B to create a user (not the ...
Read more >
Castle DynamicProxy - a dirty trick to call invocation Proceed ...
Proceed () multiple times inside the Intercept() method of the interceptor. To verify this I setup a simple test case with an adhoc...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found