开发者

How does one dynamically add new bindings using Ninject's DI during runtime?

So I'm attempting to use a combination of MEF and Ninject in an application I'm writing. Basically I'm adding in extensions via MEF during runtime. I don't understand how (or if it's even possible to) I can update Ninject's bindings during runtime.

For example, say I have the following item imported by MEF:

[Export(typeof(ICar))]
public class BmwCar : ICar
{
    private ICarLogger _carLogger;

    public BmwCar(ICarLogger carLogger)
    {
        _carLogger = carLogger;
    }

    public static string Type
    {
        get { return "Sedan"; }
    }

    public string GetBrand()
    {
        return "BMW";
    }

    public static Type InterfaceType { get { return ICar; } }
    public static Type CarType { get { return GetType(); } }
}

Now normally if I knew this item at compile time I could just create a Ninject Module with the following bindings like:

public class NinjectSetup : NinjectModule
{
    public override void Load()
    {
        Bind<CarLogFactory>().ToSelf().InSingletonScope();
        Bind<ICarLogger>().ToMethod(x => x.Kernel.Get<CarLogFactory>(new ConstructorArgument("vehicleName", BmwCar.Type)).WhenInjectedInto<BmwCar>();
    }
}

So the problem is this line:

Bind<ICarLogger>().ToMethod(x => x.Kernel.Get<CarLogFactory>(new ConstructorArgument("vehicleName", BmwCar.Type)).WhenInjectedInto<BmwCar>();

I'm not sure how I could add something like that dynamically after BmwCar is imported. Obviously I can't use the generic at run time since type is needed during compile time. Because I can't use generics at run time 开发者_StackOverflowit seems doing something like:

var binding = new BindingBuilder<ICarLogger>(new Binding(typeof(ICarLogger)), this.Kernel).ToMethod(x => x.Kernel.Get<CarLogFactory>(new ConstructorArgument("vehicleName", imported.Type)).WhenInjectedInto<imported.CarType>();

is not an option. Anyone aware of away to create new bindings during runtime?


Yes, you can use the Bind or Rebind method available on the NInject Kernel.

I don't know anything about MEF, but the code you want might look something like this:

void OnApplicationStart()
{
    StaticKernelContainer.Kernel = new StandardKernel(new YourInjectionModule());
}

void AfterMEFHasDoneWhatItNeedsToDo()
{
    // (You may need to use Rebind at this point)
    StaticKernelContainer.Kernel.Bind<ICarLogger>().To(importer.CarType);
}


You sure you cant do something cleaner e.g. via method injection?

Also read the CreateLog example in the contextual binding wiki

I'm not convinced you need to go doing stacks of Bind<> calls or having to add lots of static helper classes like you have.

Or perhaps I've just read it wrong - can you extend your example a bit; I just dont really get it as-is to be honest?


Here is a solution which does not rely on MEF but should do what you want to achieve.

// Plugin interface assembly defines
interface ICarInfoProvider
{
    IEnumerable<string> CarTypes { get; }
}

// Plugin Bmw Assembly defines
public class BmwPluginCarInfoProvider : ICarInfoProvider
{
    IEnumerable<string> CarTypes { 
        get { return new List<string> { "Sedan", "3", "5" }; } 
    }
}

public class BmwPluginModule : NinjectModule
{
    public override Load() {
        // Or use ctor to define car name
        this.Bind<ICarInfoProvider>().To<BmwPluginCarInfoProvider>();
        this.Bind<ICar>().To<BmwCar>().Named("Sedan").OnActivation(car => car.Name = "Sedan");
        this.Bind<ICar>().To<BmwCar>().Named("3").OnActivation(car => car.Name = "3");
        this.Bind<ICar>().To<BmwCar>().Named("5").OnActivation(car => car.Name = "5");
    }
}

// Plugin Toyota Assembly defines
public class ToyotaPluginCarInfoProvider : ICarInfoProvider
{
    IEnumerable<string> CarTypes { 
        get { return new List<string> { "Yaris", "Prius", }; } 
    }
}

public class ToyotaPluginModule : NinjectModule
{
    public override Load() {
        // Or use ctor to define car name
        this.Bind<ICarInfoProvider>().To<ToyotaPluginCarInfoProvider>();
        this.Bind<ICar>().To<ToyotaCar>().Named("Yaris").OnActivation(car => car.Name = "Yaris");
        this.Bind<ICar>().To<ToyotaCar>().Named("Prius").OnActivation(car => car.Name = "Prius");
    }
}

// Application
var kernel = new StandardKernel(new NinjectSettings { 
    // Ensure here that assembly scanning is activated
 });

 public class NinjectSetup : NinjectModule
{
    public override void Load()
    {
            Bind<CarLogFactory>().ToSelf().InSingletonScope();

            // Sorry for being vague here but I'm in a hurry
            Bind<ICarLogger>().ToMethod(x => x.ContextPreservingGet<CarLogFactory>(new ConstructorArgument("vehicleName", ctx => // access here named parameter or use own parameter to get name //).CreateLogger());
    }
}

// Somewhere in your code

var infos = resolutionRoot.GetAll<ICarInfoProvider>();

// User chooses "Sedan"
var sedan = resolutionRoot.Get<ICar>("Sedan");
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜