ninjectaopninject-interception

Ninject Intercept any method with certain attribute?


How can I get Ninject.Extensions.Interception to basically let me bind a specific interceptor to any method that has an attribute... psudocode:

Kernel.Intercept(context => context.Binding.HasAttribute<TransactionAttribute>())
      .With<TransactionInterceptor>

With a class like:

public SomeClass
{
    [TransactionAttribute]
    public void SomeTransactedMethod()
    { /*do stuff */ }
}

Solution

  • Assuming that you are using Ninject.Extensions.Interception this should do the trick

    public class TransactionInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            // Do something...
        }
    }
    
    public class TransactionAttribute : InterceptAttribute
    {
        public override IInterceptor CreateInterceptor(IProxyRequest request)
        {
            return new TransactionInterceptor();
        }
    }
    
    public class SomeClass
    {
        [Transaction]
        public virtual void SomeTransactedMethod() { }
    }
    

    Make sure that the method that should be intercepted is marked as virtual.

    When SomeTransactedMethod() is called it should be intercepted.

    var kernel = new StandardKernel();
    kernel.Bind<SomeClass>().ToSelf();
    
    var someClass = kernel.Get<SomeClass>();
    someClass.SomeTransactedMethod();
    

    UPDATE

    You could create a custom planning strategy.

    public class CustomPlanningStrategy<TAttribute, TInterceptor> :
        NinjectComponent, IPlanningStrategy
            where TAttribute : Attribute
            where TInterceptor : IInterceptor
    {
        private readonly IAdviceFactory adviceFactory;
        private readonly IAdviceRegistry adviceRegistry;
    
        public CustomPlanningStrategy(
            IAdviceFactory adviceFactory, IAdviceRegistry adviceRegistry)
            {
                this.adviceFactory = adviceFactory;
                this.adviceRegistry = adviceRegistry;
            }
    
            public void Execute(IPlan plan)
            {
                var methods = GetCandidateMethods(plan.Type);
                foreach (var method in methods)
                {
                    var attributes = method.GetCustomAttributes(
                        typeof(TAttribute), true) as TAttribute[];
                    if (attributes.Length == 0)
                    {
                        continue;
                    }
    
                    var advice = adviceFactory.Create(method);
    
                    advice.Callback = request => request.Kernel.Get<TInterceptor>();
                    adviceRegistry.Register(advice);
    
                    if (!plan.Has<ProxyDirective>())
                    {
                        plan.Add(new ProxyDirective());
                    }
                }
            }
        }
    
        private static IEnumerable<MethodInfo> GetCandidateMethods(Type type)
        {
            var methods = type.GetMethods(
                BindingFlags.Public | 
                BindingFlags.NonPublic | 
                BindingFlags.Instance
            );
    
            return methods.Where(ShouldIntercept);
        }
    
        private static bool ShouldIntercept(MethodInfo methodInfo)
        {
            return methodInfo.DeclaringType != typeof(object) &&
                   !methodInfo.IsPrivate &&
                   !methodInfo.IsFinal;
        }
    }
    

    This should now work.

    var kernel = new StandardKernel();
    kernel.Components.Add<IPlanningStrategy, 
        CustomPlanningStrategy<TransactionAttribute, TransactionInterceptor>>();
    
    kernel.Bind<SomeClass>().ToSelf();
    
    var someClass = kernel.Get<SomeClass>();
    someClass.SomeTransactedMethod();