Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ninject Method-level interception with params

I've noticed in the tutorials for interception that you can target a method and intercept it. I.e.

 Kernel.Bind<Foo>().ToSelf();
 Kernel.InterceptReplace<Foo>(foo => foo.ThrowsAnError(), invocation => {} );

The documentation/tutorial does not cover what to do in the instance that the method you're trying to intercept has parameters i.e if ThrowsAnError accepted a string as a parameter.

 Kernel.Bind<Foo>().ToSelf();
 Kernel.InterceptReplace<Foo>(foo => foo.ThrowsAnError(**param goes here**), invocation => {} );

At the time of binding I do not have access to the params so I was wondering whether I am going about this the wrong way?

Edit

Working example

like image 743
Mark Walsh Avatar asked Nov 06 '12 09:11

Mark Walsh


1 Answers

I think you are misunderstanding what happens. Your Foo object is replaced with an decorator that contains the interceptor. Here is a simplistic example:

public class FooDecorator : Foo
{
    private readonly Foo decorated;

    public FooDecorator(Foo foo) { this.decorated = foo; }

    public void ThrowsAnError(object param1, int param2)
    {
        // calls the decorated instance with supplied parameters
        this.decorated.ThrowsAnError(param1, param2);
    }
}

In other words, the parameters that are supplied when the resolved Foo is called, will be passed on to the decorated instance.

With interception however, this is all a bit more indirect (and slower), but the concept is the same. I must admit that I'm not familiar with Ninject interception, but there is probably a Proceed method on the invocation object. In other words, you should do something like this:

Kernel.InterceptReplace<Foo>(foo => foo.ThrowsAnError(),
    invocation =>
    {
        try
        {
            // calls the decorated instance with supplied parameters
            invocation.Proceed();
        }
        catch (Exception ex)
        {
            Kernel.Get<ILogger>().Log(ex);
        }
    } );

UPDATE

I assume that the first argument of the InterceptReplace<T> method is not an delegate, but an expression tree, such as Expression<Action<T>>. This method is in fact not called, but it is analyzed to find out which method to intercept. In other words, since the method is never called, you can just supply any argument you which. The trick is to let the C# compiler know which method overload (if any) to use. It doesn't matter if you supply rubbish. When both arguments are reference types, this will probably work:

Kernel.InterceptReplace<Foo>(foo => foo.ThrowsAnError(null, null),
like image 63
Steven Avatar answered Oct 04 '22 23:10

Steven