Search code examples
c#attributesninjectexplicit-interface

Is it possible to inject into explicitly implemented method using Ninject?


Here is what I would like to do:

public interface IInject<in T>
{
    [Inject]
    void Inject(T reference);
}

private class Foo : IInject<Bar>
{
    public Bar Bar { get; private set; }

    void IInject<Bar>.Inject(Bar reference)
    {
        Bar = reference;
    }
}

But nothing gets injected, only way that I got to work is with attribute and implicit implementation:

private class Foo : IInject<Bar>
{
    public Bar Bar { get; private set; }

    [Inject]
    public void Inject(Bar reference)
    {
        Bar = reference;
    }
}

Is there a way to do it?


Solution

  • Ninject doesn't behave like that by default. You will need to create a custom selector.*

    Given your types this selector exhibits the behavior you've described,

    class ExplicitSelector : Selector
    {
        public ExplicitSelector(
            IConstructorScorer constructorScorer, 
            IEnumerable<IInjectionHeuristic> injectionHeuristics) 
            : base(constructorScorer, injectionHeuristics)
        {
        }
    
        public override IEnumerable<MethodInfo> SelectMethodsForInjection(Type type)
        {
            // Gets all implemented interface and grabs an InterfaceMapping. 
            var implementedInterfaces = type.GetInterfaces();
    
            foreach (var map in implementedInterfaces.Select(type.GetInterfaceMap))
            {
                for (var i = 0; i < map.InterfaceMethods.Length; i++)
                {
                    // Check each interface method for the Inject attribute, and if present
                    if (map.InterfaceMethods[i].CustomAttributes.Any(x => x.AttributeType == typeof (InjectAttribute)))
                    {
                        // return the target method implementing the interface method. 
                        yield return map.TargetMethods[i];
                    }
                }
            }
    
            // Defer to NInject's implementation for other bindings. 
            foreach (var mi in base.SelectMethodsForInjection(type))
            {
                yield return mi;
            }
        }
    }
    

    It's added to the kernel simply,

    standardKernel.Components.Remove<ISelector, Selector>();
    standardKernel.Components.Add<ISelector, ExplicitSelector>();
    

    And then calls to get an IInject<Bar> will work as you describe, using the custom selector.


    *There may be a better extension point to implement this with NInject. I'm far from an expert on NInject or it's implementation.