Search code examples
c#ninjectinterceptorconventionninject-interception

Intercepting on an Interface


I am trying to make something like an IAuditable interface, which acts as a marker for Ninject to intercept calls to.

Suppose I have the following:

public interface IAuditable
{

}

public interface IProcessor
{
    void Process(object o);
}

public class Processor : IProcessor, IAuditable
{
    public void Process(object o)
    {
        Console.WriteLine("Processor called with argument " + o.ToString());
    }
}

With this setup:

NinjectSettings settings = new NinjectSettings() { LoadExtensions = true };
IKernel kernel = new StandardKernel(settings);
kernel.Bind<IAuditAggregator>().To<AuditAggregator>().InThreadScope();
kernel.Bind<IAuditInterceptor>().To<AuditInterceptor>();

kernel.Bind(x =>
            x.FromThisAssembly()
            .SelectAllClasses()
            .InheritedFrom<IAuditable>()
            .BindToDefaultInterfaces() //I suspect I need something else here
            .Configure(c => c.Intercept().With<IAuditInterceptor>()));
kernel.Bind<IProcessor>().To<Processor>();

Whenever I try to kernel.Get<IProcessor>(); I get an exception telling me there are multiple bindings available.

If I remove kernel.Bind<IProcessor>().To<Processor>() then it works as expected, but it is possible that you can have an IProcessor that does not implement IAuditable.

Am I on the right track?

Edit: As suggested I tried using an attribute instead:

public class AuditableAttribute : Attribute
{

}
[Auditable]
public class Processor : IProcessor
{

    public void Process(object o)
    {
        Console.WriteLine("Processor called with argument " + o.ToString());
    }
}
//in setup:
kernel.Bind(x =>
            x.FromThisAssembly()
            .SelectAllClasses()
            .WithAttribute<AuditableAttribute>()
            .BindDefaultInterface()
            .Configure(c => c.Intercept().With<IAuditInterceptor>()));

This results in the same duplicate binding issue as with using an interface instead.


Solution

  • You should be able to write one convention binding for types implementing IAuditable and one for types not implementing.

            kernel.Bind(x =>
                x.FromThisAssembly()
                    .SelectAllClasses()
                    .InheritedFrom<IAuditable>()
                    .BindDefaultInterfaces()
                    .Configure(c => c.Intercept().With<IAuditInterceptor>()));
    
            kernel.Bind(x =>
                x.FromThisAssembly()
                    .SelectAllClasses()
                    .InheritedFrom<IProcessor>()
                    .Where(t => !typeof(IAuditable).IsAssignableFrom(t))
                    .BindDefaultInterfaces());