Как динамически добавлять новые привязки, используя DI Ninject во время выполнения? - PullRequest
4 голосов
/ 31 марта 2011

Итак, я пытаюсь использовать комбинацию 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>();

I 'Я не уверен, как я могу добавить что-то подобное динамически после импорта 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 ]

3 голосов
/ 31 марта 2011

Да, вы можете использовать метод Bind или Rebind, доступный в ядре NInject.

Я ничего не знаю о 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 голос
/ 01 апреля 2011

Вот решение, которое не зависит от 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");
1 голос
/ 31 марта 2011

Вы уверены, что не можете сделать что-то более чистое, например, через метод инъекции ?

Также прочитайте пример CreateLog в вики контекстной привязки

Я не уверен, что вам нужно делать стеки из Bind<> вызовов или добавлять множество статических вспомогательных классов, как у вас.

Или, может быть, я просто неправильно прочитал - не могли бы вы немного расширить свой пример? Я просто не понимаю, если честно?

...