2

I'm trying to understand how Ninject.Extensions.Interception 3.0.0.8 is building dynamic proxies for my classes. I've found that when I decorate my concrete classes with an attribute that inherits from InterceptAttribute or when I directly Intercept at binding time with Intercept() method then Ninject returns a Dynamic Proxy of the decorated class instead of the normal type.

I have a IPolicySearchPresenter interface which I'm binding to FlexPolicySearchPresenter concrete type adding an Exception logger interceptor:

Bind<IExceptionInterceptor>().To<ExceptionInterceptor>();
Bind<IPolicySearchPresenter>().To<FlexPolicySearchPresenter>().Intercept().With<IExceptionInterceptor>();

The problem is that when I inspect the returning type for that binding:

var proxy = Kernel.Get<IPolicySearchPresenter>();

I get an instance of Castle.Proxies.IPolicySearchPresenterProxy instead of FlexPolicySearchPresenterProxy

This is giving me problems with my FluorineFx remoting app. However, if I create my Castle Proxy manually:

ProxyGenerator generator = new ProxyGenerator();
    //My presenter type
    Type type = typeof(FlexPolicySearchPresenter);
    //My presenter interface
    var interfaceType = type.GetInterfaces().Single();
    //Get my Interceptor from container. Notice that i had to 
    //change my Interceptor to implement IInterceptor from Castle libs,
    // instead of Ninject IInterceptor
    var excepInt = Kernel.Get<ExceptionInterceptor>();
    //Manually get all my instances required by my presenter type Constructor
    //ideally passed through Constructor Injection
    var presenterSearchService = Kernel.Get<IPolicySearchService>();
    var userAuthService = Kernel.Get<IUserAuthorizationService>();
    //Create proxy, passing interceptor(s) and constructor arguments
    var proxy = generator.CreateClassProxy(type, new object[] { presenterSearchService, userAuthService },
            new IInterceptor[]
        {
            excepInt
        });
    //Ninject.Extensions.Interception.DynamicProxyModule
    // I'm using directive ToConstant(..), and not To(..)
    //Bind my interface to the new proxy
    Bind(interfaceType).ToConstant(proxy).InThreadScope();

var proxy = Kernel.Get<IPolicySearchPresenter>();

The returning types come back as Castle.Proxies.FlexPolicySearchPresenterProxy which work perfectly with my remoting implementation.

The question is, how can I get Ninject.Interception return me instances of FlexPolicySearchPresenterProxy instead of IPolicySearchPresenterProxy. Notice that by doing the manual Castle way I am binding in a different way:

        Bind(interfaceType).ToConstant(proxy).InThreadScope();

Instead of the ninject way:

Bind<IPolicySearchPresenter>().To<FlexPolicySearchPresenter>().Intercept().With<IExceptionInterceptor>();

Do I need to change the way I'm doing the Binding in Ninject to get the right type?

Adolfo Perez
  • 2,834
  • 4
  • 41
  • 61
  • Have you tried creating a binding from IPolicy.. to FlexPolicy *and* then binding FlexPolicy .ToSelf().Intercept().With... ? (Sidenote: ninject instanciaties interceptors once per type and not once per proxied instance). – BatteryBackupUnit Oct 14 '13 at 09:27
  • No, I haven't tried that. I'll give it a try this morning. Thanks for the suggestion – Adolfo Perez Oct 14 '13 at 09:45
  • I did this: `Bind().To();` `Bind().ToSelf().Intercept().With();` `var proxy = Kernel.Get();` But proxy is comming back as `FlexPolicySearchPresenter` type instead of `FlexPolicySearchPresenterProxy` and interception didn't kick in :( – Adolfo Perez Oct 14 '13 at 11:39
  • Strange. Are there any virtual methods @ FlexPolicySearchPresenter? btw., here is the ninject extension code for dynamic proxy instanciation: https://github.com/ninject/ninject.extensions.interception/blob/master/src/Ninject.Extensions.Interception.DynamicProxy/DynamicProxyProxyFactory.cs (note the if(targetType.IsInterface)) – BatteryBackupUnit Oct 14 '13 at 11:49
  • Yes I have a virtual method in my presenter where I'm throwing an exception so my Interceptor manages it. I downloaded Ninject.Extensions.Interception source code for the release I'm using from NuGet but can't get to attach a debugger to it, that would make things much clearer... – Adolfo Perez Oct 14 '13 at 12:20
  • @BatteryBackupUnit what would be my target type in my case? I'm a little confused: `Type targetType = context.Request.Service; if (targetType.IsInterface)` – Adolfo Perez Oct 14 '13 at 12:24
  • Request.Service is what you want to resolve. So if you have a ctor argument IFoo it's IFoo. If the ctor argument is Foo, then it's Foo. In your case it is IPolicySearchPresenter. – BatteryBackupUnit Oct 14 '13 at 14:20
  • Regarding debugger: Putting the source @ the following path C:\Projects\Ninject\ninject.extensions.XYZ might be a simple solution. – BatteryBackupUnit Oct 14 '13 at 14:24

1 Answers1

1

Edit: added property injection to Foo.

I've got a working solution for you, but to be honest, i'm not 100% happy about it. Anyway, this works:

class Program
{
    static void Main(string[] args)
    {
        var kernel = new StandardKernel();
        kernel.Bind<IFoo>().ToMethod(ctx => ctx.Kernel.Get<Foo>());

        kernel.Bind<Foo>().ToSelf().Intercept().With<SomeInterceptor>();

        var foo = kernel.Get<IFoo>();

        foo.DoSomething();

        Console.WriteLine(foo.GetType());

        Console.Read();
    }
}

public interface IFoo
{
    void DoSomething();
}

public class Foo : IFoo
{
    [Inject]
    public Bar Dependency { get; set; }

    public virtual void DoSomething()
    {
        Console.WriteLine("doing something with {0}", this.Dependency);
    }
}

public class SomeInterceptor : IInterceptor
{
    public SomeInterceptor()
    {
        Console.WriteLine("interceptor created");
    }

    public void Intercept(IInvocation invocation)
    {
        Console.WriteLine("before");
        invocation.Proceed();
        Console.WriteLine("after");
    }
}

public class Bar
{
    public override string ToString()
    {
        return "Bar (injected dependency)";
    }
}

The resulting output is:

interceptor created
before
doing something with Bar (injected dependency)
after

And the type is:

Castle.Proxies.FooProxy

It seems that .Bind().To() and .Bind().ToSelf().Intercept... do not have the same result. I don't know why (yet) - but maybe i'm going to investigate it.

Update on constructor arguments: Ninject by itself only supports "inheritance based class proxy" - where the class needs a default / empty ctor and "interface proxy without target" - which is what you don't want.

Therefore, would it be acceptable for you to use property injection "just this once"? Otherwise you will need to create your own interception ninject-magic and use "class proxy with target" (see http://docs.castleproject.org/Tools.Kinds-of-proxy-objects.ashx) Remark: Even though "class proxy with target" supports constructor arguments you need to know which they are beforehand (so no easy DI support). (I did not find a hook for resolving constructor arguments after dynamic proxy chooses/creates the constructor for the proxy.)

BatteryBackupUnit
  • 12,934
  • 1
  • 42
  • 68
  • I'll try it as soon as my Visual Studio comes back to life. Thanks for your help! – Adolfo Perez Oct 14 '13 at 15:10
  • Didn't work for me. I get this error: "Could not find a parameterless constructor." My FlexPolicySearchPresenter is a little more complex, has constructor injection: `public FlexPolicySearchPresenter(IPolicySearchService policySearchServiceService, IUserAuthorizationService iUserAuthorizationService) : base(iUserAuthorizationService) { _policySearchService = policySearchServiceService; }` – Adolfo Perez Oct 14 '13 at 15:18
  • Yes it comes from Castle.DynamicProxy: at Castle.DynamicProxy.ProxyGenerator.CreateClassProxyInstance(Type proxyType, List`1 proxyArguments, Type classToProxy, Object[] constructorArguments) at Castle.DynamicProxy.ProxyGenerator.CreateClassProxy(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, Object[] constructorArguments, IInterceptor[] interceptors) at Castle.DynamicProxy.ProxyGenerator.CreateClassProxy(Type classToProxy, ProxyGenerationOptions options, Object[] constructorArguments, IInterceptor[] interceptors) – Adolfo Perez Oct 14 '13 at 16:18
  • See bottom on updated answer for more details on the "empty constructor" problem. – BatteryBackupUnit Oct 15 '13 at 08:19
  • Thanks @BatteryBackupUnit. At this point I don't think property injection is an option, we're trying to stay away form it. The way i'm creating the Class Proxy manually through Castle libs is currently working fine. But I wanted to get the job done through Ninject extension wrappers. – Adolfo Perez Oct 15 '13 at 11:38