Ostatnio poznaję kontener DI Autofac i baaardzo mi się on podoba – nie tylko nazwa, funkcjonalność także. Ten post rozpoczyna paczkę kilku ciekawych (mam nadzieję:) ) postów pokazujących, jak przy pomocy Autofac zbudować “samoskładającą się” aplikację.
Dzisiaj na dobry początek wrócimy do koncepcji, którą zerżnąłem od Udiego a przedstawiłem w poście Application Events (tam tez więcej linków w temacie samych zdarzeń). W opisanym przeze mnie sposobie mechanizm ten wymagał ręcznej rejestracji każdego handlera za pomocą klasy ApplicationEventsManager, na przykład tak:
1: ApplicationEventsManager.OnEvent<UserForgotPassword>().UseHandler<SendReminder>();
Mając takie rejestracje w momencie wywołania zdarzenia:
1: ApplicationEventsManager.Raise(new UserForgotPassword(user));
Manager przejeżdża się po prostu po swojej wewnętrznej liście handlerów i wykonuje te, które potrafią dane zdarzenie obsłużyć:
1: public static class ApplicationEventsManager 2: { 3: private static readonly IList<object> _handlers = new SynchronizedCollection<object>(); 4: 5: public static void Raise<TEvent>(TEvent e) where TEvent : IApplicationEvent 6: { 7: foreach (var handler in _handlers.OfType<Handles<TEvent>>()) 8: { 9: handler..Handle(e); 10: } 11: } 12: //...
(po więcej kodu i wyjaśnień zapraszam oczywiście do wspomnianej notki)
Działa, fajnie wygląda, daje dużą kontrolę, ale można by spróbować inaczej. Po co ręcznie rejestrować handlery? O tego właśnie mamy przecież kontenery DI/IoC! Na dobrą sprawę kod CAŁEJ klasy ApplicationEventsManager, pozbawionej instrukcji pozwalających na ręczną rejestrację, mógłby wyglądać tak:
1: public static class ApplicationEventsManager 2: { 3: public static void Raise<T>(T e) where T : IApplicationEvent 4: { 5: foreach (var handler in Container.Resolve<IEnumerable<Handles<T>>>()) 6: { 7: handler.Handle(e); 8: } 9: } 10: }
Nic prostszego! Co prawda takie coś nawet na miano “managera” nie zasługuje, ale…:).
Z Aufoaq wystarczy zaimplementować klasę modułu, która wykryje wszystkie implementacje handlerów z wykorzystaniem otwartych typów generycznych. Brzmi groźnie? To zobaczmy:
1: public class ApplicationEventsRegistrationModule : Module 2: { 3: protected override void Load(ContainerBuilder builder) 4: { 5: builder 6: .RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies()) 7: .AsClosedTypesOf(typeof(Handles<>)); 8: } 9: }
I.. to wszystko! Taki moduł dodajemy do Autofac, a reszta dzieje się sama. Każdy handler w całym systemie (wszystkich załadowanych dllkach) będzie widoczny – dzięki metodzie RegisterAssemblyTypes. Piękne.
Po więcej info o open generic types odsyłam do Google’a.
Nie bylbym soba, gdybym nie wspomnial, ze Autofa nie jest tu wyjatkiem i Castle Windsor tez fantastycznie radzi sobie ze wspomnianym zagadnieniem, o czym nawet niedawno blogowalem ;)
@Krzysztof Koźmic:
Nie wątpię:). Wczesniej znalem tylko Unity i postanowilem (w sumie także za sprawą twoich postów i tweetów) poszerzyc horyzonty. Kolejność: Autofac -> Ninject -> Castle.
Dość wygodna sprawa z tą automatyzacją. Ale jak to bywa ktoś już zauważył braki Unity i stąd np. http://autoregistration.codeplex.com/
AppDomain.CurrentDomain.GetAssemblies() ??
A co z assemblies, które nie są załadowane?
Tak z ciekawości chętnie zobaczę jakby to wyglądało ze skanowaniem np. folderu bin w ASP.NET.
StructureMap ma ponoć bardzo duże możliwości skanowania, natomiast Ninject ma na nim wzorowany tzw. extension.
@r:
Rozwinięcie tej koncepcji przedstawiłem w kolejnym poście ( http://www.maciejaniserowicz.com/post/2010/04/01/Samobudujaca-sie-aplikacja-z-Autofac.aspx ), gdzie do AppDomain ładuję wszystkie pliki z rozszerzeniem dll.