Castle Windsor: container.Install(FromAssembly.This()) – Občas nefunguje? Pozor na inlining!

Dneska nás tu zabavil problém s chováním Castle Windsor.

Symptom

Na jednom z projektů Castle Windsor „občas“ ignoroval registrace a odmítal resolvovat komponenty, které měly být regulérně zaregistrovány pomocí installerů. Při vývoji a ladění z Visual Studia na vývojářských strojích vše fungovalo, po continous integration deploymentu na STAGE aplikace nejela. Padala na chybu:

System.ApplicationException: Error in resolving dependency XY. --->
 Castle.MicroKernel.ComponentNotFoundException: No component for supporting the service XY was found

Celé se to chovalo velmi nedeterministicky. Při re-deploymentu stejného buildu aplikace pomocí XCOPY se tato rozběhla, po iisreset zase nešla, atp.

Analysis

Vzhledem k tomu, že aplikace se chovala jinak na vývojářských strojích (fungovala v DEBUG i RELEASE) a jinak na STAGE, bylo potřeba zvolit trochu agresivnější metody ladění. Pokus o remote-debugging vypadal zdlouhavě, analýza memory dumpu ukázala, že ve WindsorContaineru příslušné registrace opravdu chybí.

Zkoumání registrací/installerů nakonec ukázalo, kde je problém.

Cause

Uspořádání solution bylo jednoduché. Webová aplikace, v Global.asax:Application_Start se vytvoří container a následně se na něm zavolá extension-metoda ConfigureForWebApplication(), která samotná je však v samostatné class-library WindsorInstallers, kde jsou umístěné všechny installery a extension-metoda si je volá přes Install(FromAssembly.This()):

public static void ConfigureForWebApplication(this IWindsorContainer container)
{
	container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel));
	container.AddFacility();
	container.Install(FromAssembly.This());
}

Kritickým a zároveň podezření-hodným místem je právě řádek s installem. Rychlý náhled do zdrojáků FromAssembly.This() a konzultace s MSDN ohledně Assembly.GetCallingAssembly potvrzuje podezření na inlining a zmatečné určení assembly s installery. Metoda ConfigureForWebApplication() byla inlinována a GetCallingAssembly() tak vracelo jako assembly tu, v které je Global.asax:Application_Start().

Action

Náprava je naštěstí jednoduchá, metodu ConfigureForWebApplication() je potřeba označit atributem MethodIml pro compiler tak, aby inlining neproběhl:

[MethodImpl(MethodImplOptions.NoInlining)]
public static void ConfigureForWebApplication(this IWindsorContainer container)
{
	container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel));

	container.AddFacility();

	container.Install(FromAssembly.This());
}

Alternativně lze samozřejmě zdrojovou assembly určit jiným determinističtějším způsobem (FromAssembly.jinak()).

Zanechat odpověď

Vyplňte detaily níže nebo klikněte na ikonu pro přihlášení:

Logo WordPress.com

Komentujete pomocí vašeho WordPress.com účtu. Odhlásit /  Změnit )

Twitter picture

Komentujete pomocí vašeho Twitter účtu. Odhlásit /  Změnit )

Facebook photo

Komentujete pomocí vašeho Facebook účtu. Odhlásit /  Změnit )

Připojování k %s