Mówili mi ludzie, czemu sam rejestrujesz zależności w autofacu, skoro można skorzystać z konwencji, sam wszystko rozpozna, wykryje co masz załadowane do pamięci i z tego rozkmini. Jak się domyślacie wpis ten wziął się stąd, że nie zawsze tak się dzieje.
Otóż tak stworzona rejestracja czasem powoduje problemy:
Problemy wygladają tak:
None of the constructors found with ‘Autofac.Core.Activators.Reflection.DefaultConstructorFinder’ on type ‘IsThereAnyNews.Mvc.Controllers.HomeController’ can be invoked with the available services and parameters: Cannot resolve parameter ‘IsThereAnyNews.DataAccess.IRssChannelsRepository rssRepository’ of constructor ‘Void .ctor(IsThereAnyNews.Services.IUserAuthentication, IsThereAnyNews.Services.ILoginService, IsThereAnyNews.Services.ISessionProvider, IsThereAnyNews.DataAccess.IRssChannelsRepository)’.
Brak zarejestrowanej implementacji klasy, ale jak to nie ma skoro napisałem wcześniej żeby się automagicznie samo się. Na szczęście na stackoverflow są mądrzy ludzi i w dziesięć minut mi to wytłumaczyli.
AppDomain.CurrentDomain.GetAssemblies() call. According to MSDN the method:
Gets the assemblies that have been loaded into the execution context of this application domain.
Czyli co jakiś czas (najczęściej po restarcie IIS), projekt z MVC się ładuje, ale nie załadował wszystkich referencji jeszcze, więc nie podczas rejestracji autofaca ten wykrył interfejsów oraz implementacji, przez to nie zrobił pełnej rejestracji.
I uwaga, po mojemu to tak, że jeśli nie ma interfejsów, to nie trzeba szukać do nich implementacji, a skoro ich (tych interfejsów) nie ma to nie ma też błędów czy ostrzeżeń. I dalej autofac się robił i rejestrował, teraz gdy generowane było żądanie http do kontrolera, ten przychodził do autofaca a ten naburmuszony mówił: “jaka implementacja? jaki interfejs? gdzie mi tu z łapami! dawaj domyślny konstruktor! Jak to? Nie masz?! Gleba i EXCEPTION!” – resztę historii już znacie, żółty ekran na prodzie – fak je.
Ale żeby nie płakać, to rozwiązanie jest takie proste:
Tutaj należy zwrócić uwagę na linię (@5) gdzie ładowane są referencje. Taki sposób pobierać ma listę wszystkich referencji, najwyraźniej nawet tych jeszcze nie załadowanych. A zatem, nawet jeśli moje *services czy *repositories nie będą załadowane, to będą widnieć na liście referencji, a ta zostanie przekazana do autofaca do skanowania i na pewno zostanie wczytana, co wreszcie spowoduje że nie będzie miauczeć że nie zna typa czy jego implementacji. Oddech i dziękuje.
A ja to się ostatnio zastanawiałem nad tym. Sam kiedyś namiętnie korzystałem z autorejestracji w Autofac i Windsor i chyba jeszcze jakimś tam. No i wszystko jest proste jak masz tylko XxController, YyRepository lub ZzService. Ale jak zacznie się człowiek zastanawiać chwilę na nazwami i pojawi się w kodzie jakiś Processor, Transformer, Adapter, Visitor, Translator, Factory i Jon Skeet jeden wie co jeszcze, to całe to magiczne rejestrowanie bierze w łeb.
Nie mówiąc o lifecycle. Bo autorejestrując wszystko as-is dostejesz same transienty, czy same singleton, a może wszystkie typy są per-request? A niestety często trzeba się zastanowić i precyzyjnie zdefiniować czas życia obiektu z kontenera. Niestety, magia o precyzji kuli ognia piekielnego wcale nie pomaga
Jasne że trzeba myśleć, ale sporo rzeczy odpada. I możesz się skupić na tych wyjątkach. Trochę jak z automapperem – też w zasadzie wszystko robi sam, tylko ciężkie przypadki trzeba ręcznie dopisać.
Tomek dziękuje za czytanie.