如何在运行时使用Ninject的DI动态添加新绑定?

时间:2011-03-31 03:03:29

标签: c# dependency-injection runtime mef ninject

所以我试图在我正在编写的应用程序中使用MEF和Ninject的组合。基本上我在运行时通过MEF添加扩展。我不明白如何(或者甚至可能)我可以在运行时更新Ninject的绑定。

例如,假设我有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(); } }
}

现在通常如果我在编译时知道这个项目,我可以创建一个带有以下绑定的Ninject模块:

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>();
    }
}

问题就在这一行:

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

我不确定如何在导入BmwCar后动态添加类似的内容。显然我不能在运行时使用泛型,因为在编译期间需要类型。因为我不能在运行时使用泛型,所以它似乎做了类似的事情:

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>();

不是一种选择。有人知道在运行期间创建新绑定吗?

3 个答案:

答案 0 :(得分:3)

是的,您可以使用NInject Kernel上提供的Bind或Rebind方法。

我对MEF一无所知,但你想要的代码看起来像这样:

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);
}

答案 1 :(得分:1)

你确定你不能做一些更清洁的事情,例如通过method injection

另请阅读the CreateLog example in the contextual binding wiki

我不相信你需要进行Bind<>调用的堆栈,或者必须像你一样添加许多静态助手类。

或许我只是读错了 - 你能延长你的榜样吗?说实话,我真的不明白吗?

答案 2 :(得分:1)

这是一个不依赖于MEF但应该做你想要实现的解决方案。

// 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");