Ninject Intercept any method with certain attribute?
Asked Answered
E

2

14

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 */ }
}
Ekaterinoslav answered 17/6, 2011 at 13:29 Comment(0)
P
15

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();
Piliform answered 17/6, 2011 at 20:7 Comment(5)
Cant have my attributes inheriting from InterceptAttribute, as the domain where the transaction attribute is defined has no knowledge of the DI concerns... Which is why I need to do it via the Kernel.Intercept method, which is defined within the application layer.Ekaterinoslav
Ok, I see now... I've made a crude implementation of IPlanningStrategy that hopefully will do the trick. You might want to consult the actual code of Ninject.Extensions.Inteception on Github for ways to improve the custom planning strategy.Piliform
Hmm seems a bit more work than what I was after... I was sure a while back I saw an example of just a simple one liner using the Intercept extension method on Kernel, will give it another search on the internet and if all else fails try the above... thanks.Ekaterinoslav
Seems there is no other way I could find, so using your method. Thanks for taking the time to write it up, will give you the answer!Ekaterinoslav
You can simply call the .Intercept().With<SomeInterceptor>() instead of this bunch on codes AFAIK. take a look at Ninject Interception Extensions.Whitacre
W
1

Here is the code that I used for the same purpose

//Code in Bind Module
this.Bind(typeof(ServiceBase<,>))
    .ToSelf()
    .InRequestScope()
    .Intercept()
    .With<TransactionInterceptor>();

And

public class TransactionInterceptor : IInterceptor
{
    #region Constants and Fields

    public ISession session;
    private ISessionFactory sessionFactory;

    #endregion

    #region Constructors and Destructors

    public TransactionInterceptor(ISession session, ISessionFactory sessionFactory)
    {
        this.sessionFactory = sessionFactory;
        this.session = session;
    }


    #endregion

    public void Intercept(IInvocation invocation)
    {
        try
        {
            if (!session.IsConnected)
                session = sessionFactory.OpenSession();

            session.BeginTransaction();
            invocation.Proceed();
            if (this.session == null)
            {
                return;
            }

            if (!this.session.Transaction.IsActive)
            {
                return;
            }
            else
            {
                this.session.Transaction.Commit();
            }
        }
        catch (Exception)
        {
            if (this.session == null)
            {
                return;
            }

            if (!this.session.Transaction.IsActive)
            {
                return;
            }

            this.session.Transaction.Rollback();

            throw;
        }
    }
}

And code for TransactionAttribute

public class TransactionAttribute : InterceptAttribute
{
    #region Public Methods

    public override IInterceptor CreateInterceptor(IProxyRequest request)
    {
        return request.Context.Kernel.Get<TransactionInterceptor>() ;
    }

    #endregion
}
Whitacre answered 2/12, 2011 at 8:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.