2

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?

Mathieu Guindon
  • 69,817
  • 8
  • 107
  • 235
  • unrelated just wanted to say congrats on the MVP, was checking out your profile and saw you got it this year... was it a hard interview/application process after the nomination? – Kubie Oct 24 '18 at 16:41
  • @Kubie thanks! the form took a couple of days to fill up (you need to enumerate all your eligible contributions for the year - I gotta update my stuff for *this* year now), then there was a phone interview, ..then I woke up on Jan 1st with a congratulations email from Microsoft! =) – Mathieu Guindon Oct 24 '18 at 17:14

1 Answers1

0

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).

Yacoub Massad
  • 27,509
  • 2
  • 36
  • 62