使用Ninject进行方法级别的拦截,带有参数。

7

我注意到在拦截教程中,你可以针对一个方法进行拦截。例如:

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

文档/教程没有涵盖在您尝试拦截的方法具有参数的情况下该怎么办,例如,如果 ThrowsAnError 接受一个字符串作为参数。

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

在绑定时,我无法访问参数,因此我想知道我是否走错了路?

编辑

工作示例

1个回答

4

我觉得你理解错了发生的事情。你的Foo对象被替换成了一个包含拦截器的装饰器。这里是一个简单的例子:

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);
    }
}

换句话说,在调用已解析的Foo时提供的参数将传递给装饰实例。

然而,使用拦截器时,这一切都有些间接(并且更慢),但概念是相同的。我必须承认,我不熟悉Ninject拦截,但invocation对象上可能有一个Proceed方法。换句话说,您应该这样做:

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);
        }
    } );

更新

我假设InterceptReplace<T>方法的第一个参数不是委托,而是表达式树,例如Expression<Action<T>>。 实际上,此方法并没有被调用,只是被分析以找出要拦截的方法。换句话说,由于该方法从未被调用,您可以随意提供任何参数。关键在于让C#编译器知道使用哪个方法重载(如果有)。如果两个参数都是引用类型,那么这可能会起作用,无论您提供什么内容。

Kernel.InterceptReplace<Foo>(foo => foo.ThrowsAnError(null, null),

1
我明白你的意思,但我认为OP会有问题绑定到那个方法,因为Kernel.InterceptReplace<Foo>(foo => foo.ThrowsAnError(),...);需要在其中输入参数,否则它不会编译,因为ThrowsAnError需要N个参数,对于你的情况,你必须像这样绑定:Kernel.InterceptReplace<Foo>(foo => foo.ThrowsAnError(somehowSatisfyParam1, somehowSatisfyParam2), ...);虽然我可能是错的。 - Grofit
1
Grofit是正确的,foo.ThrowsAnError()仍然需要指定参数。 - Mark Walsh
1
谢谢,Steven。我会在今晚的测试项目中尝试这个,并更新你结果。 - Mark Walsh
2
Steven,你关于参数传递是正确的,请查看我编辑过的具有工作测试版本。 - Mark Walsh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接