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.