castleproject / Core

Castle Core, including Castle DynamicProxy, Logging Services and DictionaryAdapter
http://www.castleproject.org/
Other
2.2k stars 467 forks source link

Calls to other methods within the same class are not being intercepted #646

Closed tskong closed 1 year ago

tskong commented 1 year ago

Hi,

I'm using autofac interceptors which use castle.core to perform the interception. I raised this on their github and they directed me here. I'm hoping someone here can help out.


I've upgraded to the latest Autofac and the main project from .net 4.7.2 to .net core 6, and I''ve noticed a change in behaviour. With the old version, it used to intercept all methods to a class. e.g. If a method called another method within that class, both would be intercepted.

Is this a change in intended change of behaviour, or is this a bug?

Steps to Reproduce

Create asp.net core 6 MVC project.

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());

builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
{
    builder.Register(c => new TestInterceptor());

    builder.RegisterType<Calculator>()
    .As<ICalculator>()
    .EnableInterfaceInterceptors()
    .InterceptedBy(typeof(TestInterceptor));
});

Then the Test Interceptor

    public class TestInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            invocation.Proceed();
        }
    }

The class

  public class Calculator : ICalculator
    {
        public int Add(int a, int b)
        {
            return a + b;
        }

        public int Subtract(int a, int b)
        {
            return a - b;
        }

        public int SomeMaths(int a, int b)
        {
            var x = Add(5, 2);
            return Subtract(x, 3);

        }
    }

And finally the Controller method

        public HomeController(ICalculator calc)
        {
            _calc = calc;
            var a =_calc.Add(1, 2); // intercepted
            var b = _calc.Subtract(4, 2);   // intercepted
            var c = _calc.SomeMaths(10, 11); // intercepted, but it does not intercept Add() or Subtract()

        }

Expected Behavior

For Add() and Subtract() to be intercepted when called from SomeMethod()

Exception with Stack Trace

n/a

Dependency Versions

<PackageReference Include="Autofac" Version="6.5.0" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Autofac.Extras.DynamicProxy" Version="6.0.1" />

Additional Info

n/a

stakx commented 1 year ago

I don't know Autofac Interceptors so I can't really help much.

I can tell you, however, that DynamicProxy is unable to intercept sealed methods such as your Calculator class' Add and Subtract. Try making them virtual and see what happens.

Depending on how exactly Autofac Interceptors makes use of DynamicProxy, they might even be able to get this to work for you automatically by re-implementing ICalculator, then intercept those interface methods, and possibly forward to the Calculator class' implementations if so desired. But you'd need to have a discussion about that with their team.

tskong commented 1 year ago

It does intercept Add() and Subtract() even though they aren't virtual, which goes against what's in the documentation.

Infact, it intercepts all public methods on Calculator(), except when one of these methods invokes another public method inside the same class. It only appears to intercept the very first call.

I did try making them all virtual but the behaviour is the same.

stakx commented 1 year ago

If Autofac Interceptors intercepts your sealed Calculator methods (or at least you think that this is what it does), it is much more likely that it actually reimplements ICalculator in the proxy subclass (which derives from Calculator) and intercepts those methods. If these methods then delegate to the base class implementation, and those base class methods call each other (while seeing this typed as Calculator instead of ICalculator), then interception is effectively bypassed, since the method implementations that kick off the interception sit in the more derived proxy subclass and the base class implementations never call those.

This isn't a problem with DynamicProxy, but with the specific usage pattern found in Autofac Interceptors. I have to refer you back to their team, there's nothing wrong with DynamicProxy if my guess is correct, and there's nothing we can do here.

tskong commented 1 year ago

Hi, thanks for looking into this. After much investigation over the weekend, I discovered that the interception was working to spec. The sub interception I had been told by a more experienced developer for this application wasn't actually happening, and there was other application code in play.

Sorry for wasting your time on this, but it was very interesting looking through the source.