4

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.

ldam
  • 4,412
  • 6
  • 45
  • 76
  • 1
    I think in that case the use of an [Auditable] attribute is better than using a marker-interface. – Steven Aug 04 '17 at 13:45
  • I suppose you can write a conditional statement inside the `Configure` lambda that calls `Intercept()` only when the type implements the interface (or is marked with the attribute). – Steven Aug 04 '17 at 13:47
  • Using an attribute results in the same outcome, a duplicate binding is created. – ldam Aug 04 '17 at 13:48
  • 1
    Btw, about the use of `InThreadScope`: https://stackoverflow.com/a/14592419/264697 – Steven Aug 04 '17 at 13:50
  • I am not sure I understand what your problem is. You are using `SelectAllClasses` which effectively picks up `Processor` because of the marker interface or the attribute. You then "manually" add another binding at the bottom so of course it's going to complain. – JuanR Aug 04 '17 at 13:55
  • Yes, I don't know how to avoid that. Basically I need to bind everything that does have the interface/attribute separately from those that don't. – ldam Aug 04 '17 at 13:56
  • But your code already works that way, I think. By adding your manual binding at the bottom, you are mapping a class that was already bound because it implements the interface. – JuanR Aug 04 '17 at 14:19
  • The single line "manual" binding is there to demonstrate the problem in a simple application. In reality I will be using `kernel.Bind(x => x.FromThisAssembly().SelectAllClasses().InheritedFrom().BindDefaultInterface().Configure(c => c.InSingletonScope()));`. That will handle all "normal" cases where I don't want things to be audited, but will not install the interceptor for cases where I do. – ldam Aug 04 '17 at 14:25
  • This sounds like a job for @ChrisShain – hoodaticus Aug 04 '17 at 16:23

1 Answers1

1

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());
Jan Muncinsky
  • 4,282
  • 4
  • 22
  • 40