5

I will describe my environment: I have Ninject + Ninject Interception Extension working to enable auto registration of interceptors for all methods, marked with a special attribute. It is a common AoP + attributes + DI container scenario.

My problem is: When porting to latest version of Ninject and Ninject Interception Extension - 3.0 I start to get an exception when my interceptors are supposed to run. My InterceptorRegistrationStrategy works fine when resolving the attributed type and registering interceptors. But running the intercepted method results in following exception:

System.ArgumentException : Interface not found.
at System.RuntimeTypeHandle.VerifyInterfaceIsImplemented(RuntimeTypeHandle handle, RuntimeTypeHandle interfaceHandle)
at System.RuntimeType.GetInterfaceMap(Type ifaceType)
at Ninject.Extensions.Interception.Advice.Advice.MatchesMethod(IProxyRequest request)
at System.Linq.Enumerable.WhereListIterator`1.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList(IEnumerable`1 source)
at Ninject.Extensions.Interception.Registry.AdviceRegistry.GetInterceptorsForRequest(IProxyRequest request)
at Ninject.Extensions.Interception.Registry.AdviceRegistry.GetInterceptors(IProxyRequest request)
at Ninject.Extensions.Interception.Wrapper.StandardWrapper.CreateInvocation(IProxyRequest request)
at Ninject.Extensions.Interception.Wrapper.DynamicProxyWrapper.Intercept(IInvocation castleInvocation)
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Infrastructure.Tests.Persistance.Conversations.NinjectConversationInterceptorBehavior.ShouldCreateInterceptorOnImplicitConversation() in NinjectConversationInterceptorBehavior.cs: line 74 

I'm kinda left to resort to Reflector and using Ninject Interception Extension sources to do something about this problem, paired with not enough documentation it leaves me in a bad position.

Anyone got the same exception when porting to Ninject 3.0?

Here's the code I use to register the interceptors based on the attribute automatically:

public class NinjectConversationInterceptorRegistrationStrategy : InterceptorRegistrationStrategy
{
    public NinjectConversationInterceptorRegistrationStrategy(IAdviceFactory adviceFactory,
                                                              IAdviceRegistry adviceRegistry)
        : base(adviceFactory, adviceRegistry)
    {
    }

    public override void Execute(IPlan plan)
    {
        var pcAttribute = plan.Type.GetOneAttribute<PersistenceConversationalAttribute>();

        if (pcAttribute != null)
        {
            if (pcAttribute.MethodsIncludeMode == MethodsIncludeMode.Implicit)
            {
                foreach (var mi in GetCandidateMethods(plan.Type))
                {
                    RegisterMethodInterceptors(plan.Type, mi);
                    if (!plan.Has<ProxyDirective>())
                    {
                        plan.Add(new ProxyDirective());
                    }
                }
            }
            else
            {
                foreach (
                    var mi in
                        GetCandidateMethods(plan.Type).Where(
                            mi => mi.HasAttribute<PersistenceConversationAttribute>()))
                {
                    if (!mi.IsVirtual)
                    {
                        throw new InvalidOperationException(
                            string.Format("[PersistentCoversation] attribute used on non-virtual method {0}.{1}",
                                          mi.DeclaringType.Name,
                                          mi.Name));
                    }
                    RegisterMethodInterceptors(plan.Type, mi);
                    if (!plan.Has<ProxyDirective>())
                    {
                        plan.Add(new ProxyDirective());
                    }
                }
            }
        }
    }

    protected virtual void RegisterMethodInterceptors(Type type, MethodInfo method)
    {
        IAdvice advice = this.AdviceFactory.Create(method);
        advice.Callback = GetIntercepor;
        this.AdviceRegistry.Register(advice);
    }

    protected virtual IInterceptor GetIntercepor(IProxyRequest arg)
    {
        var interceptor = new NinjectConversationLazyInterceptor(arg.Kernel);
        return interceptor;
    }

    protected override bool ShouldIntercept(MethodInfo methodInfo)
    {
        if (IsPropertySetter(methodInfo))
        {
            return false;
        }
        var ret = base.ShouldIntercept(methodInfo);
        return ret;
    }

    private static bool IsPropertySetter(MethodBase methodInfo)
    {
        return methodInfo.IsSpecialName && methodInfo.Name.StartsWith("set_");
    }
}
Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
Cortlendt
  • 2,190
  • 4
  • 29
  • 50
  • I'm also using this: http://stackoverflow.com/questions/5353476/ninject-one-interceptor-instance-per-one-class-instance-being-intercepted – Cortlendt Apr 18 '12 at 10:01
  • Update - i'm resolving the type that has interceptors around it by the interface it implements and it looks like resolving the by interface is to blame for this exception. I'm not completely sure, but I think line of code - "InterfaceMapping interfaceMap = this.method.DeclaringType.GetInterfaceMap(request.Method.DeclaringType);" in Advice class is the source of exceptions, to be more precise - its parameters. – Cortlendt Apr 18 '12 at 12:16
  • Please add a bug report on github attaching a project demonstrating the problem. – Remo Gloor Apr 20 '12 at 08:08
  • I've made a simplified test project and it worked fine. Looks like it is an integration problem with components I use in production. I will update or close this question upon debugging the production code more. – Cortlendt Apr 20 '12 at 10:52
  • I found the problem. Base class of the intercepted type has a method, that is not a member of the interface that intercepted type implements. Though this method of base class it being implicitly registered for interception. I have no idea whether it is a bug or not. It worked in previous version though. Link for test project: http://ge.tt/4bqPYbG/v/0?c – Cortlendt Apr 20 '12 at 11:44

2 Answers2

3

The behavior of interception changed: the extension will create an interface proxy when an interface is injected rather than a class proxy because this has the advantage that the methods don't need to be virtual anymore. Either you have to put it to the interface, exclude the method from interception (it is useless to intercept a method that can't be called anyway) or inject a class instead of an interface

Remo Gloor
  • 32,665
  • 4
  • 68
  • 98
1

Issue moved to project's git hub: issue

Cortlendt
  • 2,190
  • 4
  • 29
  • 50