Mam łańcuch zależności, który wygląda mniej więcej tak:Ninject Bind Kiedy przodek typ T
public class CarSalesBatchJob
{
public CarSalesBatchJob(IFileProvider fileProvider)
{ ... }
}
public class MotorcycleSalesBatchJob
{
public MotorcycleSalesBatchJob(IFileProvider fileProvider)
{ ... }
}
public class FtpFileProvider : IFileProvider
{
public FtpFileProvider(IFtpSettings settings)
{ ... }
}
public class CarSalesFtpSettings : IFtpSettings { ... }
public class MotorcycleSalesFtpSettings : IFtpSettings { ... }
Do tej pory używam konwencje oparte wiązania, ale to nie jest wystarczająco dobre więcej, ponieważ mam więcej niż jedną implementację dla IFtpSettings
. Zdecydowałem się więc użyć niektórych kontekstowych wiązań. Na pierwszy rzut oka kernel.Bind<>().To<>().WhenInjectedInto<>()
wyglądał obiecująco, ale to tylko pomaga na pierwszym poziomie, co oznacza, że jeśli miałem CarSalesFtpFileProvider
i MotorcycleSalesFtpProvider
mogę to zrobić:
kernel.Bind<IFtpSettings>().To<CarSalesFtpSettings>()
.WhenInjectedInto<CarSalesFtpFileProvider>();
kernel.Bind<IFtpSettings>().To<MotorcycleSalesFtpSettings>()
.WhenInjectedInto<MotorcycleSalesFtpFileProvider>();
Ale wydaje się dość głupi, aby utworzyć dwie konkretne implementacje FtpFileProvider
tak naprawdę różnią się tylko tym, jakich ustawień chcę użyć. Widziałem, że istnieje metoda o nazwie WhenAnyAnchestorNamed(string name)
. Ale ta trasa wymaga od mnie nakładania atrybutów i magicznych ciągów na moje zadania wsadowe, których nie jestem zachwycony.
Zauważyłem również, że nie jest to zwykły stary .When(Func<IRequest, bool>)
metoda na wiążące deklaracje, więc wpadłem na to jak my wiążących stwierdzeń:
//at this point I've already ran the conventions based bindings code so I need to unbind
kernel.Unbind<IFtpSettings>();
kernel.Bind<IFtpSettings>().To<CarSalesFtpSettings>()
.When(r => HasAncestorOfType<CarSalesBatchJob>(r));
kernel.Bind<IFtpSettings>().To<MotorcycleSalesFtpSettings>()
.When(r => HasAncestorOfType<MotorcycleSalesBatchJob>(r));
// later on in the same class
private static bool HasAncestorOfType<T>(IRequest request)
{
if (request == null)
return false;
if (request.Service == typeof(T))
return true;
return HasAncestorOfType<T>(request.ParentRequest);
}
Więc jeśli konstruktor prosi o IFtpSettings, my recurse się żądanie drzewo, aby sprawdzić, czy którakolwiek z żądanych usług/typów w łańcuchu jest zgodna z podanym typem (CarSalesBatchJob lub MotorcycleSalesBatchJob), a jeśli tak, zwróci true. Jeśli dojdziemy do końca łańcucha, zwracamy fałsz.
Przepraszamy za długie objaśnienie tła.
Oto moje pytanie: czy istnieje jakikolwiek powód, dla którego nie powinienem podejść do problemu w ten sposób? Czy uważa się to za złą formę? Czy istnieje lepszy sposób na znalezienie typów żądań przodków? Czy powinienem zrestrukturyzować moje klasy/łańcuch zależności w bardziej "przyjemny" sposób?
Uważam, że rozwiązanie, które zidentyfikowałeś w swoim pytaniu, aby zaimplementować dwie konkretne klasy, które łączą FtpFileProvider z implementacją IFtpSettings, jest najłatwiejsze do zrozumienia/odczytu/debugowania/przekazania. Inne rozwiązania wymagają od programisty wiedzy na temat Twojego konkretnego narzędzia IOC i sposobu jego konfiguracji. Ta implementacja jest również zgodna z Konwencją dotyczącą konfiguracji programu Ninject. –