Ninject Bind When Ancestor Of Type T

狂风中的少年 提交于 2019-12-04 01:22:40
Remo Gloor

You should use request.Target.Member.ReflectedType instead of request.Service This is the implementation type.

Also WhenAnyAncestorNamed does not require Attributes. You can mark the bindings of your Jobs using the Named method.

This is not really an answer to your question, but you can solve your problem by writing a single class as follows:

private sealed class FtpFileProvider<TFileProvider>
     : FtpFileProvider
    where TFileProvider : IFileProvider
{
    public FtpFileProvider(TFileProvider settings)
        : base(settings) { }
}

In that case, your configuration would look like this:

kernel.Bind<IFileProvider>()
    .To<FtpFileProvider<CarSalesFtpSettings>>()
    .WhenInjectedInto<CarSalesBatchJob>();

kernel.Bind<IFileProvider>()
    .To<FtpFileProvider<MotorcycleSalesFtpSettings>>()
    .WhenInjectedInto<MotorcycleSalesBatchJob>();

Please note that in my experience, I found out that in most cases where you think you need context based injection, you actually have a flaw in your design. However, with the given information, it is impossible for me to say anything about this in your case, but you might want to take a look at your code. You might be able to refactor your code in such way that you actually don't need context based injection.

My advice is to target the destination of the convention-breaking situation with your registration, not the IFtpSettings itself. For example, in a similar situation I would do the following:

container.Register<CarSalesBatchJob>(() => {
    ICommonSetting myCarSpecificDependency = container.Resolve<CarSpecificDependency>();
    new CarSalesBatchJob(myCarSpecificDependency);
});

container.Register<MotorcycleSalesBatchJob>(() => {
    ICommonSetting myMotorcycleSpecificDependency = container.Resolve<MotorcycleSpecificDependency>();
    new MotorcycleSalesBatchJob(myMotorcycleSpecificDependency);
});

This is about as straight-forward as it can get when it comes to explaining to other programmers how each Batch Job is instantiated. Instead of targeting the registration of ICommonSetting to try to handle each one-off, you handle each one-off in its own case.

To put it another way, imagine if these classes had two dependencies that needed to be altered in the IoC container. You'd have four lines of registration code in different places, but they'd all for the purpose of instantiating a MotorcycleSalesBatchJob, or CarSalesBatchJob, etc. If someone wanted to find out how the class was referenced, he'd have to hunt for any reference to a class (or a base class). Why not just write code that explains exactly how each of these are to be instantiated, all in one place?

The drawback to this (or at least what I've heard from others) is that if the constructor for either of these concrete classes change, then the code will break and you'll have to alter the registration. Well, to me, that's a positive, because I've already taken one step down this type of path with the IoC container changing based on some sort of state, I need to make sure I'm still retaining the expected behavior.

It gets even more fun when you think about the possibilities. You could do something like this:

container.Register<IProductCatalog>(() => {
    currentState = container.Resolve<ICurrentState>().GetTheState();
    if (currentState.HasSpecialPricing())
       return container.Resolve<SpecialPricingProductCatalog>();
    return container.Resolve<RegularPricingProductCatalog>();
});

All of the complexity of how things might work in different situations can be broken into separate classes, leaving it to the IoC container to supply the correct class in the right situation.

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