Property Injection in Base Controller using Ninject 2

假装没事ソ 提交于 2019-12-04 08:52:38
Ruben Bartelink

Convention-based binding lives in http://github.com/ninject/ninject.extensions.conventions -- one implements IBindingGenerator. This is largely concerned with discovering interfaces and services though.

In general, constructor injection is a good default approach. However the manner in which ASP.NET MVC works makes this harder to do (Hence FubuMVC etc.). So property injection is the next best option.

You may find that using OnActivation in your Bind may allow you to do enough - and if you can, this is by far the simplest.

I'd characterise what you're trying to do as convention-based activation. The problem's are:

  • deciding what you are going to auto-inject. Are you going to inject everything public that isn't concrete? Everything that your Kernel knows about? Unless you can come up with a clean definition of what you want to do, the injection process can become unpredictable and difficult to understand. You end up debugging and explaining to colleagues a lot.

  • making it efficient. Ninject dynamically generates code behind the scenes to make the activation of an instance efficient (i.e., at the time of walking the class looking for [Inject] markers it generates code once to do that which then gets jitted as if you'd written it longhand).

Looking in the code, there's no easy way OOTB. Looks like adding a custom IInjectionHeuristic would do the trick.

However if you're getting this deep into containers, you need to

  1. pause and see if you can keep it simple by not going down this road
  2. go to the ninject mailing list and search for similar things
  3. if you still want to do it, send a mail there.

Expanding on the ideas by Ruben Bartelink you could create a custom implementation of IInjectionHeuristic.

public class ControllerInjectionHeuristic : NinjectComponent, IInjectionHeuristic
{
    private readonly IKernel kernel;

    public BaseControllerInjectionHeuristic(IKernel kernel)
    {
        this.kernel = kernel;
    }

    public bool ShouldInject(MemberInfo member)
    {
        if (member.ReflectedType != typeof(BaseController))
        {
            return false;
        }

        var propertyInfo = member.ReflectedType.GetProperty(member.Name);
        object service = kernel.TryGet(propertyInfo.PropertyType);

        return service != null;
    }
}

The ControllerInjectionHeuristic will inject any property (service) on BaseController for which the kernel is able to resolve the service.

Register the custom implementation with the kernel.

var kernel = new StandardKernel();
kernel.Components.Add<IInjectionHeuristic, ControllerInjectionHeuristic>();

Another solution to the problem is to use OnActivation. (This solution is a untested, but it should give you an idea on how to proceed).

public class ControllerModule : NinjectModule
{
    public override void Load()
    {
        // Get all controller types. You could use
        // Ninject.Extensions.Conventions.
        IEnumerable<Type> controllerTypes = null;
        foreach (var controllerType in controllerTypes)
        {
            Bind(controllerType).ToSelf().InRequestScope()
                .OnActivation(ControllerActivation);
        }
    }

    private static void ControllerActivation(IContext context, object obj)
    {
        var controller = obj as BaseController;
        if (controller == null)
        {
            return;
        }

        controller.ProductService = context.Kernel.Get<IProductService>();
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!