Ninject Method-level interception with params
Asked Answered
N

1

7

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

Neanderthal answered 6/11, 2012 at 9:53 Comment(0)
V
4

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),
Valladares answered 6/11, 2012 at 13:25 Comment(4)
I see where you are coming from, but I think the OP will have a problem binding to that method as the Kernel.InterceptReplace<Foo>(foo => foo.ThrowsAnError(),...); would require arguments to be entered in there, otherwise it wont compile as ThrowsAnError requires N arguments right, in your instance you would have to bind like so I think. Kernel.InterceptReplace<Foo>(foo => foo.ThrowsAnError(somehowSatisfyParam1, somehowSatisfyParam2), ...); I could be wrong though.Haileyhailfellowwellmet
Grofit is correct, the foo.ThrowsAnError() will still expect params to be stipulatedNeanderthal
Thanks, Steven I will try this on my test project tonight and update you with the outcome.Neanderthal
Steven you were correct regarding the argument passing, please see my edit with a working test version.Neanderthal

© 2022 - 2024 — McMap. All rights reserved.