2011-03-31 4 views
4

그래서 내가 쓰고있는 응용 프로그램에서 MEF와 Ninject의 조합을 사용하려고 시도하고 있습니다. 기본적으로 런타임 중에 MEF를 통해 확장 기능을 추가합니다. 어떻게 런타임에 Ninject의 바인딩을 업데이트 할 수 있는지 이해할 수 없습니다.런타임 중에 Ninject의 DI를 사용하여 새로운 바인딩을 어떻게 동적으로 추가합니까?

[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에 모듈을 만들 수 컴파일시에이 항목을 알고있는 경우 :

예를 들어

, 나는 다음과 같은 항목은 MEF에서 가져온 말 내가 COU 방법을 잘 모르겠어요

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

:

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>(); 
    } 
} 
그래서 문제는이 라인 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>(); 

은 옵션이 아닙니다. 누구든지 런타임 중에 새로운 바인딩을 만들 수 있다는 것을 알고 있습니까?

+0

제네릭 매개 변수를 사용할 필요가 없습니다. To 메서드에는 정상 매개 변수로 Type을 받아들이는 오버로드가 있습니다. 내 대답을 참조하십시오. – cbp

+0

'return ICar; 대신'return typeof (ICar);를 사용 했습니까? – Steven

답변

3

예, 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); 
} 
+0

그래서 나는 그것이 그렇게 단순하다는 것을 깨닫지 못했습니다. 방금 var bind = new Binding (typeof (BmwCar));을 사용하여 거기에서 오버로드를 수정했습니다. 일단 제대로 설정되면, 나는 단지'Kernel'을 추가했다. 고마워요 cbp! – ymerej

1

method injection을 통해?

또한 당신이 Bind<> 호출 스택을하고 또는 당신이 가지고있는 것처럼 정적 도우미 클래스를 많이 추가 할 필요 갈 필요가 확신 아니에요

the CreateLog example in the contextual binding wiki을 참조하십시오.

아니면 그냥 잘못 읽은 것입니다. 예를 약간 확장 할 수 있습니까? 솔직히 말해서 나는 그것을 얻지 못한다.

+0

Ruben, 나는 단지 짧은 것을 지키기 위해 정적 도우미 만 사용하고 있습니다. 나는 옳은 길에 나를 가지고 있다고 믿지만, 문제는 Bind와 관련이있는 것 같다. MEF를 사용하면 런타임까지 유형이 어떻게 될지 알 수 없습니다. 따라서 알 수없는 유형의 인터페이스를 어떻게 바인딩 할 수 있는지 잘 모르겠습니다. 예를 들어, ICar을 BmwCar에 바인드하려면 다음과 같이하면된다 :'Bind () .To () .InSingletonScope;'누군가가 MEF 가져 오기 라이브러리를 감시 대상에 넣을 때까지 BmwCar이 무엇인지 모르기 때문에 그럴 수 없다. 디렉토리에 저장되고 주 프로그램이 실행 중일 때 새 오브젝트 BmwCar가 반입됩니다. – ymerej

+0

@j h :'To '은 컴파일 타임에 정적 유형을 알아야합니다.2)'Bind .ToMethod (ctx => CreateMeTheIGivenTheContext (ctx)) '타입을 아는 경우이 두 가지 주요 경로가 Bind .To (Type)'에서 떨어져 있습니다. 접근 방식 2를 취하면 바인딩이 등록되는 경우와 그렇지 않은 경우 모두 관리 할 수 ​​있습니다. 이는 가능한 모든 IMO에서 가능한 한 많은 poreferable입니다. [멀티 인젝션] (https://github.com/ninject/ninject/wiki/Multi-injection)을 원한다면 일반적으로 여러 개의 '바인드 (Bind)'호출을 원합니다. 당신은 dirs을 보았다) –

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"); 
+0

이 솔루션의 유일한 문제점은 런타임에 모든 데이터 유형을 알고 있다고 가정한다는 것이다. 필자의 경우, 지정된 디렉토리 카탈로그로 실행중인 MEF 앱이있을 수 있습니다. 이제 누군가가'BmwCar'를 포함하는 라이브러리를 내 '감시 대상'디렉토리에 드롭한다고 가정 해보십시오. MEF는 그것을 가져 와서'BmwCar'를 가져오고'BmwCar'에 기반한 바인딩을 등록하고 싶습니다. 그러나 Ninject는 뭔가를 놓치지 않는 한 generics를 통해서만 이것을 허용합니다. – ymerej

+0

Hy 아주 정확하지 않습니다. 데모 용도로 BmwCar에 바인딩을 추가했습니다. ICarLogger의 바인딩은 실제 유형을 완전히 알지 못합니다. 하지만 지금 당신이 달성하기를 원하는 것을 봅니다. 답변은 다음과 같습니다. –

관련 문제