How to access an extended interface on an interceptor?

143 views Asked by At

We have a multi-binding defined in a NinjectModule for some IInspection interface, like this:

private void BindCodeInspectionTypes()
{
    var inspections = Assembly.GetExecutingAssembly()
                              .GetTypes()
                              .Where(type => type.BaseType == typeof (InspectionBase));

    // multibinding for IEnumerable<IInspection> dependency
    foreach (var inspection in inspections)
    {
        var binding = Bind<IInspection>().To(inspection).InSingletonScope();
        binding.Intercept().With<TimedCallLoggerInterceptor>();
        binding.Intercept().With<EnumerableCounterInterceptor<InspectionResultBase>>();
    }
}

So the interceptor proxy types will be for IInspection. However some of the inspection types implement an IParseTreeInspection interface, which extends IInspection:

public interface IParseTreeInspection : IInspection
{
    ParseTreeResults ParseTreeResults { get; set; }
}

The problem this creates is with this bit of code that consumes the interceptors - the injected proxy types understandably don't seem to know anything about IParseTreeInspection, so this foreach loop doesn't iterate even once:

var enabledParseTreeInspections = _inspections.Where(inspection => 
    inspection.Severity != CodeInspectionSeverity.DoNotShow 
 && inspection is IParseTreeInspection);

foreach (var parseTreeInspection in enabledParseTreeInspections)
{
    (parseTreeInspection as IParseTreeInspection).ParseTreeResults = parseTreeWalkResults;
}

Is there any way I can multi-bind IInspection (i.e. constructor-inject IEnumerable<IInspection>) and still be able to tell IParseTreeInspection instances when Ninject is injecting interceptors?

1

There are 1 answers

0
Yacoub Massad On

Here is one way to do it:

foreach (var inspection in inspections)
{
    if (typeof (IParseTreeInspection).IsAssignableFrom(inspection))
    {
        var binding = Bind<IParseTreeInspection>()
            .To(inspection)
            .InSingletonScope()
            .Named(inspection.FullName);

        binding.Intercept().With<TimedCallLoggerInterceptor>();
        binding.Intercept().With<EnumerableCounterInterceptor<InspectionResultBase>>();

        Bind<IInspection>().ToMethod(
            c => c.Kernel.Get<IParseTreeInspection>(inspection.FullName));
    }
    else
    {
        var binding = Bind<IInspection>().To(inspection).InSingletonScope();
        binding.Intercept().With<TimedCallLoggerInterceptor>();
        binding.Intercept().With<EnumerableCounterInterceptor<InspectionResultBase>>();
    }
}

This code checks if the type is assignable from IParseTreeInspection, i.e., it implements IParseTreeInspection, and if so, it creates a named binding from IParseTreeInspection to this type and configures the interception here. This will make sure that the proxy objects (created by the interceptor) will implement IParseTreeInspection. Then it creates another binding from IInspection to a delegate invocation that would use the kernel to resolve IParseTreeInspection via the the first binding (using the binding name).