Co załadowane nie jest nie pisze się w rejestr

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.

Jak ja tych PMów

pm

Pamiętam gdy byłem młodszy, myślałem sobie że taki PM to co najmniej darmozjad. Siedzi na dupsku i czyta internet. Czasem zajrzy do pokoju gdzie krew, pot, łzy. Gdzie bohaterowie oddają swoje życie na froncie. Gdzie dzieje się prawdziwa praca, a komputery piszczą z rozkoszy kompilacji. I zamarudzi taki, czemu to jeszcze nie działa, a czemu wolno, a miało być, a jak tu kliknę to źle się dzieje, godzin nie zaraportowaliście, źle je zaraportowaliście i tak marudzi, a potem idzie z innymi sobie podobnymi na kawkę pośmiać się ze swoich sucharków. Continue reading

Tajne wpisy w app.config #dajsiepoznac

Jeśli nie chcesz mojej zguby…

Jeśli nie chcesz swojej zguby, nie wrzucaj do repozytorium sekretów swojej aplikacji. Można to osiągnąć w kilku prostych krokach, wystarczy że stworzysz w VS osobny plik z konfiguracją np. “secrets.config“, który może wyglądać tak:

Następnie zaciągniesz go w głównym web.configu w taki sposób:

W VS należy mieć go dodanego do projektu, właściwości ustawione powinny być na content i no-copy, tak samo jak w przypadku web.config. Od teraz zawartość secrets będzie wstrzykiwany do web.config podczas uruchomienia.
Jeszcze tylko w gicie warto dodać go do ignorowanych plików i można spokojnie wrzucać kod na serwer bez potrzeby pamiętania, aby zawsze odznaczyć secrets.config z listy plików.

Natomiast warto pamiętać, aby po każdej zmianie zrobić sobie backup. Tak aby nie stracić go przez przypadek.

Redirect to anchor w asp mvc

Od jakiegoś czasu pracuje za prawdziwe złoto jako sieciowy programista, dawno nikt nie wymagał aby po przekierowaniu wrócić do jakiegoś specyficznego kawałka strony. Zawsze kończyło się przekierowaniem do pełnej. Zapomniałem już o takiej funkcjonalność, no prawie zapomniałem. Otóż klepiąc sobie coś tam w domu, chciałem po zrobieniu POSTa wrócić gdzieś na dół strony, akcja nie korzysta ze zdobyczy technologi jaką jest AJAX, więc strona się przeładowywuje. Pozostało mi tylko skorzystanie z elementu html, który posiada znacznik id oraz nawigacja do strony z podaniem tego znacznika w url. Znacznik podaje się po znaczku ‘#’. Kompilator nie chciał tego przyjąć do wiadomości, gdy próbowałem zrobić this.RedirectToAction(“Index”,”Home”,new{#=”comments”}); nie działało.
Na szczęście internety dają radę także i tym razem super hero w postaci stack overflow ułatwił życie.
Uwaga podaję odpowiedź:
this.Redirect(Url.RouteUrl(new { controller = “Home”, action = “Index”}) + “#comments”);
To tyle w tym krótkim odcinku. Jutro znowu do pracy.

Metody rozszerzające – testowanie i porządek w api.

Jak każdemu porządnemu developerowi zdarza mi się czasem napisać testy. Jak każdemu porządnemu developerowi, czasem zdarza mi się wykorzystać mechanizm metod rozszerzających (jeśli nie wiesz o czym mówię sprawdź na msdn). Jak każdy prawdziwy developer, chciałem przetestować logikę, która była wykorzystywana w jednej z takich metod. W zasadzie to nie w samej metodzie, chciałem sprawdzić czy zostanie wywołana z wartościami, które są dla mnie ważne.
Zacznę od metod rozszerzających, a testowanie przyjdzie samo.

Tak wygląda interfejs udostępniony przez samą (bez metod rozszerzających) klasę List:

Aby przewinąć listę na sam dół wystarczą trzy wciśnięcia Page Down. Jak widać jedyny import, z którego korzystam to System.Collections.Generic, wymagany aby móc skorzystać z List

To się stanie gdy dodam System.Linq: (obrazek się troszkę nie mieści)

Pojawi się nowa funkcjonalność, zdefiniowana w System.Linq, która pozwala robić cuda z listą i innymi IEnumerable (jak również innymi obiektami). Po imporcie aby przewinąć listę metod, trzeba osiem razy walnąć w Page Down.

Co zrobić, aby lista nowych funkcjonalności nie zaśmiecała dostępnego api? Można to wszystko ukryć przy pomocy jednej klasy, która przekierowuje żądania dalej, do właściwej części, gdzie zdefiniowane będą rozszerzenia
Normalnie implementacja rozszerzeń może wyglądać jakoś tak:

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: Consolas, “Courier New”, Courier, Monospace;
background-color: #ffffff;
/*white-space: pre;*/
}

.csharpcode pre { margin: 0em; }

.csharpcode .rem { color: #008000; }

.csharpcode .kwrd { color: #0000ff; }

.csharpcode .str { color: #a31515; }

.csharpcode .op { color: #0000c0; }

.csharpcode .preproc { color: #cc6633; }

.csharpcode .asp { background-color: #ffff00; }

.csharpcode .html { color: #800000; }

.csharpcode .attr { color: #ff0000; }

.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}

.csharpcode .lnum { color: #606060; }

   1:  namespace ConsoleApplication
   2:  {
   3:   
   4:      class Program
   5:      {
   6:          static void Main(string[] args)
   7:          {
   8:              MyClass mc = new MyClass();
   9:          }
  10:      }
  11:   
  12:      public static class MyClassExtension
  13:      {
  14:          public static void Foo0(this MyClass target) { }
  15:          public static void Foo1(this MyClass target) { }
  16:          public static void Foo2(this MyClass target) { }
  17:          public static void Foo3(this MyClass target) { }
  18:          public static void Foo4(this MyClass target) { }
  19:          public static void Foo5(this MyClass target) { }
  20:          public static void Foo6(this MyClass target) { }
  21:          public static void Foo7(this MyClass target) { }
  22:          public static void Foo8(this MyClass target) { }
  23:          public static void Foo9(this MyClass target) { }
  24:      }
  25:   
  26:      public class MyClass
  27:      {
  28:      }
  29:  }

Po takiej implementacji otrzymujemy cztery metody podstawowe, oraz dodatkowy dziesięć nowych z rozszerzenia w MyClassExtension.

Można trochę to wszystko uprościć dodając odpowiednią warstwę abstrakcji (abstrakcja was uwolni, zapamiętajcie to). Opis poniżej:

   1:  namespace ConsoleApplication
   2:  {
   3:      using System;
   4:   
   5:      class Program
   6:      {
   7:          static void Main(string[] args)
   8:          {
   9:              MyClass mc = new MyClass();
  10:              mc.ExtensionService().Foo0();
  11:          }
  12:      }
  13:   
  14:      public static class MyClassExtension
  15:      {
  16:          public static Func<MyClass, IMyClassExtensionService> ExtensionFactory { get; set; }
  17:   
  18:          static MyClassExtension()
  19:          {
  20:              ExtensionFactory = target=> new MyClassExtensionReleaseVersion(target);
  21:          }
  22:   
  23:          public static IMyClassExtensionService ExtensionService(this MyClass target)
  24:          {
  25:              return ExtensionFactory(target);
  26:          }
  27:      }
  28:   
  29:      public interface IMyClassExtensionService
  30:      {
  31:          void Foo0();
  32:          void Foo1();
  33:          void Foo2();
  34:          void Foo3();
  35:          void Foo4();
  36:          void Foo5();
  37:          void Foo6();
  38:          void Foo7();
  39:          void Foo8();
  40:          void Foo9();
  41:      }
  42:   
  43:      public class MyClassExtensionReleaseVersion : IMyClassExtensionService
  44:      {
  45:          private MyClass myclass;
  46:   
  47:          public MyClassExtensionReleaseVersion(MyClass target)
  48:          {
  49:              this.myclass = target;
  50:          }
  51:   
  52:          public void Foo0() { }
  53:          public void Foo1() { }
  54:          public void Foo2() { }
  55:          public void Foo3() { }
  56:          public void Foo4() { }
  57:          public void Foo5() { }
  58:          public void Foo6() { }
  59:          public void Foo7() { }
  60:          public void Foo8() { }
  61:          public void Foo9() { }
  62:      }
  63:   
  64:      public class MyClass
  65:      {
  66:      }
  67:  }

Tak się to prezentuje teraz:

Zacznę od linijki 29 gdzie zdefiniowany został interfejs ze wszystkimi metodami, które mają działać jako rozszerzenie dla klasy MyClass. Linia 43 to implementacja tego interfejsu, warto zauważyć że klasa ta nie jest statyczna, oraz nie posiada statycznych pól, a metody nie przyjmują parametrów. W konstruktorze otrzymuje ona obiekt, na rzecz którego ma działać. Najlepsze zostawiam na koniec, linijka 14, klasa udostępniająca rozszerzenie dla MyClass. W ExtensionService (23) wykorzystywana jest właściwość ExtensionFactory, który jest zwyczajną metodą wytwórczą, zwracającą nowy obiekt MyClassExtensionReleaseVersion, do którego przesyła obiekt, na rzecz którego ma zostać wywołana metoda rozszerzająca. Mam nadzieję, że kod tłumaczy to prościej niż ja. Linia 10 to nowy sposób na wywołanie metod rozszerzających. 
Właściwość ExtensionFactory została zdefiniowana jako publiczna, co umożliwia jego zmianę w razie potrzeby testowania. Tak samo obiekt rozszerzający jest deklarowany poprzez interfejs, w związku z czym w testach można wykorzystać np. MyClassExtensionMockVersion i sprawdzać czy klasa zachowuje się tak jak tego oczekujemy.

Ja skorzystałem z tego rozwiązania podczas testowania nawigacji w projekcie, który korzysta z PRISMa, tam do zmiany widoków wykorzystywany jest interfejs IRegionManager i kilka metod rozszerzających ten interfejs. Chciałem sprawdzić, czy po wykonaniu operacji X jedna z moich klas zarząda zmiany widoku. Jedynym sposobem, na sprawdzenie parametrów przesłanych w query było stworzenie i podstawienie własnej implementacji metod rozszerzających. Na początku testów mam taki zapis:

   1:  Capture.Common.Prism.PrismExtensions.ServiceFactory = p => new NavigationServiceMock(p);

Tak wygląda mój lipny serwis, który działa zamiast tego z Prism:

   1:  public class NavigationServiceMock : INavigation
   2:  {
   3:      private IRegionManager p;
   4:   
   5:      public NavigationServiceMock(IRegionManager p)
   6:      {
   7:          this.p = p;
   8:      }
   9:   
  10:      public IRegionManager AddToRegion(string regionName, object view)
  11:      {
  12:          throw new NotImplementedException();
  13:      }
  14:   
  15:      public IRegionManager RegisterViewWithRegion(string regionName, Func<object> getContentDelegate)
  16:      {
  17:          throw new NotImplementedException();
  18:      }
  19:   
  20:      public IRegionManager RegisterViewWithRegion(string regionName, Type viewType)
  21:      {
  22:          throw new NotImplementedException();
  23:      }
  24:   
  25:      public void RequestNavigate(string regionName, string source)
  26:      {
  27:          throw new NotImplementedException();
  28:      }
  29:   
  30:      public void RequestNavigate(string regionName, Uri source)
  31:      {
  32:          LoadTradeViewModelTest.NavigationServiceMock_RequestedNavigatedTargetRegionName = regionName;
  33:          LoadTradeViewModelTest.NavigationServiceMock_ReqestedNavigatedTargetUri = source.OriginalString;
  34:      }
  35:   
  36:      public void RequestNavigate(string regionName, string source, Action<NavigationResult> navigationCallback)
  37:      {
  38:          throw new NotImplementedException();
  39:      }
  40:   
  41:      public void RequestNavigate(string regionName, Uri source, Action<NavigationResult> navigationCallback)
  42:      {
  43:          throw new NotImplementedException();
  44:      }
  45:  }

Najważniejsze w tym wszystkim to RequestNavigate (30) gdzie zapisuje żądany region i uri. Później w teście porównuje czy są takie jak oczekiwałem. Wszystkie NotImplementedException są nie używane w testach, dlatego też mogły pozostać w takiej domyślnie oferowanej przez VS formie.
Normalna implementacja przekazuje wszystkie parametry do oryginalnej implementacji.

Pojawia się pewien problem, przy próbie stworzenie podobnego rozwiązania dla klas generycznych. Ten problem to kompilator, który cały czas twierdzi że metody rozszerzające muszą być w klasach statycznych, nie generycznych. Same metody mogą być generyczne. Aby to obejść na szybko wymyśliłem coś takiego

   1:  namespace ConsoleApplication
   2:  {
   3:      using System;
   4:      using System.Collections.Generic;
   5:   
   6:      class Program
   7:      {
   8:          static void Main(string[] args)
   9:          {
  10:              List<int> l = new List<int>();
  11:              l.GetExtension().Foo1();
  12:          }
  13:      }
  14:   
  15:      public static class ExtensionsFactory<T>
  16:      {
  17:          public static Func<List<T>, IListExtensionService> Factory { get; set; }
  18:      }
  19:   
  20:      public static class ListExtension
  21:      {
  22:          public static IListExtensionService GetExtension<T>(this List<T> target)
  23:          {
  24:              if (ExtensionsFactory<T>.Factory == null)
  25:              {
  26:                  ExtensionsFactory<T>.Factory = o => new ListExtensionSerivce<T>(o);
  27:              }
  28:   
  29:              return ExtensionsFactory<T>.Factory(target);
  30:          }
  31:      }
  32:   
  33:      public interface IListExtensionService
  34:      {
  35:          void Foo1();
  36:      }
  37:   
  38:      public class ListExtensionSerivce<T> : IListExtensionService
  39:      {
  40:          private List<T> list;
  41:   
  42:          public ListExtensionSerivce(List<T> target)
  43:          {
  44:              this.list = target;
  45:          }
  46:   
  47:          public void Foo1()
  48:          {
  49:              Console.WriteLine("foo and the list");
  50:          }
  51:      }
  52:  }

Cały trik w tym przypadku, to przeniesienie odpowiedzialności na wytwarzanie obiektów z ListExtension do ExtensionsFactory. A podczas wywołania metody, należny się upewnić że fabryka już istnieje.

Myślę że wystarczy mojego wymądrzania się na dziś. Podoba się? Nie podoba się? Macie inne sposoby? Pytania? Uwagi? Cokolwiek?

ps
Mam nadzieję, że więcej wyjaśniłem niż zakręciłem.
ps2
Jak już wszystko napiszę, to czytam czy jest ok, a potem i tak mam lekkiego stresa, że coś głupiego napisałem.