Singleton – wzorzec Schrödingera

Na dwoje babka wróżyła

Czarna owca wśród wzorców projektowych – singleton. Często nazywany antywzorcem – źle. Trochę źle. Singleton w zależności od tego jak będzie używany będzie wykazywać cechy wzorca lub antywzorca.

Antywzorzec

Singleton jako zmienna globalnie dostępna, każdy może do niego sięgnąć i korzystać. Inny, bardziej obiektowy sposób na statyczne/globalne zmienne.

Wzorzec

Continue reading

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.

Konwencje w autofacu

Część z nas jest mniej lub bardziej leniwa. Części może przeszkadzać taki zapis, a części nie.

Szczególnie część, gdzie powtarzają się różnego rodzaju serwisy i repozytoria (@7-@14) oraz (@21-@27). Co czynić, jak zrobić to samo za mniej? Konwencje i autoskanowanie assembly’ów. Jak? Poniżej prosta ściągawka: Continue reading

Autofac update – dynamiczna zmiana implementacji

Wiadomo, że każdy projekcie są testy. W części z nich są testy jednostkowe, w innych są testy integracyjne, w innych testy programistyczne – programista klika i jak działa to działa, a w jeszcze innych test na produkcji u klienta razem z milionami użytkowników. Generalnie każdy jakieś test ma. Wiadomo jak jest w projektach komercyjnych, klient chce aplikację działająca, wykonaną z najnowszymi i najlepszymi technikami, najlepiej napisaną przez juniorów, bez testerów i PM – tak najczęściej planowany jest budżet na aplikacje. Programiści czasem potrafią spełnić część tych wymagań i udaje się nam stworzyć aplikację, która jest stabilna (najczęściej), wygląda przyzwoicie, została napisana tak, że można ją rozwijać – generalnie klient jest zadowolony, a przynajmniej nie pieni się i jest w stanie zaakceptować produkt.
Dzieje się tak, ponieważ Ci programiści dzielnie przeklikują swoje wytwory, piszą możliwie dobry kod, przy kiepskich wymaganiach czy ich braku. Czasem gdy trzeba wyklikać to samo tysiąc razy, człowiek robi się leniwy, albo nie uważny i czy tego chcemy czy nie nie jesteśmy w stanie wyklikać wszystkich przypadków. Najczęściej gdy projekt jest już sporych rozmiarów, posiada skomplikowaną logikę lub wiele alternatywnych do siebie ścieżek. 
W moim przypadku naszą słabą stroną były problemy w wydajnością. Na developerskich maszynach, gdzie testowaliśmy, mieliśmy po kilka kont użytkownika, kilka, kilkadziesiąt, lub trochę ponad setkę wpisów. Generowanie danych za każdym razem gdy baza się zmieni i trzeba było ją odtwarzać od zera nie należało do przyjemnych rzeczy, nie mieliśmy migracji, więc drop a potem create. Testy selenium pojawiły się później, ale też zajmowały wieczność.
Zmierzam do tego, że łatwo byłe tego uniknąć, generując sobie masę testowych danych. Jak tego dokonać? O tym właśnie dzisiaj, odrobina kodu się także pojawi a wszystko dostępne będzie na githubie. Jak zawsze minister zdrowia ostrzega, przykłady mogą być przejaskrawione, a rozwiązanie może się okazać przydatne tylko dla mnie.
Przykładowym problememe do rozwiązania jest prosta aplikacja, która umożliwia założenie konta, zalogowanie się, oraz dodanie jakiegoś prostego wpisu tekstowego, który jest widoczny dla wszystkich. Coś jak blog, czy czat czy lista ogłoszeń, to nie jest do końca ważne.
Zacznijmy od struktury aplikacji:
Dla prostoty wszystko co potrzebne umieściłem w jednym kontrolerze. A zwinięte foldery nie mają żadnego wpływu na treść edukacyjną tego posta.
Ok, to zaczynamy. Rejestracja nowego użytkownika wygląda tak:

   1:  [HttpGet]
   2:  public ActionResult Register()
   3:  {
   4:      var viewmodel = new RegisterViewModel();
   5:      return this.View("Register", viewmodel);
   6:  }
   7:   
   8:  [HttpPost]
   9:  public ActionResult Register(RegisterDto dto)
  10:  {
  11:      this.accountService.Register(dto);
  12:      return this.RedirectToAction("Index");
  13:  }
I ostatecznie prosty serwis:

   1:  public void Register(RegisterDto dto)
   2:  {
   3:      WebSecurity.CreateUserAndAccount(
   4:          dto.Email,
   5:          dto.Password);
   6:  }
Jak widać korzystam z SimpleMembershipProvider. Klasa dto do rejestracji też jest książkowo prosta:

   1:  public class RegisterDto
   2:  {
   3:      public string Email { get; set; }
   4:      public string Password { get; set; }
   5:  }
Tak więc, aby założyć konto wystarczy wpisać dwa stringi i ciach, konto dostępne. Oczywiście gdy będzie trzeba to powtórzyć wiele razy, zacznie się to robić męczące i nudne. Dlatego leniwi programiści mogą wymyślić sobie coś takiego:

.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:  [HttpGet]
   2:  public ActionResult NewUsers()
   3:  {
   4:      this.testService.CreateNewUsers();
   5:      return this.RedirectToAction("Index");
   6:  }
Gdzie serwis został zaimplementowany w taki sposób:

   1:  public void CreateNewUsers()
   2:  {
   3:      var dto = new RegisterDto
   4:      {
   5:          Email = Faker.InternetFaker.Email() + Faker.NumberFaker.Number(),
   6:          Password = Faker.NumberFaker.Number(1000000000, 2000000000).ToString()
   7:      };
   8:      this.accountService.Register(dto);
   9:  }
Fakera opisałem w poprzednim poście. Warto zwrócić uwagę, że test serwis korzysta z account service. Dwie ważne rzeczy: każdy wie, że zależności powinny iść tylko w pionie, nigdy w poziomie – istnieje ryzyko zależności cyklicznych; jest to serwis testowy, wiec korzysta z normalnego przepływu danych, nie robi nic na skróty, nie oszukuje, także ułatwia nam tworzenie użytkowników, a jednocześnie testuje działające już serwisy.
Jak do tej pory jeszcze nie ma wielkiego szału, ale się powoli rozkręcam. Kolejny etap to wpis w aplikacji, kontroler wygląda tak:

   1:  [HttpGet]
   2:  [Authorize]
   3:  public ActionResult NewBlogPost()
   4:  {
   5:      var viewmodel = new BlogPostViewModel();
   6:      return this.View("NewBlogPost", viewmodel);
   7:  }
   8:   
   9:  [Authorize]
  10:  [HttpPost]
  11:  public ActionResult NewBlogPost(NewBlogPostDto dto)
  12:  {
  13:      this.postingService.NewBlogPost(dto);
  14:      return this.RedirectToAction("Index");
  15:  }
Kod nie powinien niczym zaskakiwać, GET i POST, oraz wymagane wcześniejsze zalogowanie się użytkownika. Warstwa implementująca:

   1:  public void NewBlogPost(NewBlogPostDto dto)
   2:  {
   3:      var userId = this.accountService.GetCurrentLoggedUserId();
   4:      var model = new BlogPost(userId, dto.Text, DateTime.Now);
   5:      this.database.BlogPosts.Add(model);
   6:      this.database.SaveChanges();
   7:  }
Dto dla nowego wpisu jest niebiańsko prosta:

   1:  public class NewBlogPostDto
   2:  {
   3:      public string Text { get; set; }
   4:  }
Ale wróćmy do serwisu, w serwisie korzystając z SMP (single membership provider) pobieramy aktualne ID użytkownika. Może nie bezpośrednio z SMP, ale prostego wrapperka nad SMP. W momencie implementacji postawiłem na opcje zaciągania danych o użytkowniku z SMP.
Sam wrapper wcześniej pokazany częściowo, teraz w calej okazałości:

   1:  class WebSecurityAccountService : IAccountService
   2:  {
   3:      public void Logout()
   4:      {
   5:          WebSecurity.Logout();
   6:      }
   7:   
   8:      public bool Login(LoginDto dto)
   9:      {
  10:          var result = WebSecurity.Login(dto.Email, dto.Password);
  11:          return result;
  12:      }
  13:   
  14:      public void Register(RegisterDto dto)
  15:      {
  16:          WebSecurity.CreateUserAndAccount(
  17:              dto.Email,
  18:              dto.Password);
  19:      }
  20:   
  21:      public int GetCurrentLoggedUserId()
  22:      {
  23:          return WebSecurity.CurrentUserId;
  24:      }   
  25:  }
No dobra, fajny pomysł, informacje o użytkowniku zawsze pobierane z serwera, wydaje się bezpieczne i w ogóle. Ale teraz dochodzimy znowu do momentu, gdy chciałbym napisać kawałek kodu który pozwoli mi jednym kliknięciem utworzyć nowy wpis. Najlepiej za każdym razem z losowym tekstem (tutaj Faker daje radę) ale jeszcze z losowego istniejącego konta. W tym momencie na chwilę zrobiło się trudno, ale skoro mamy IAccountService i z niego pobieramy informacje o użytkowniku, czemu tego nie wykorzystać, wystarczy go tylko podmienić na inny, mój, i losować z niego użytkowników:

   1:  [HttpGet]
   2:  public ActionResult NewPosts()
   3:  {
   4:      this.testService.CreateNewPosts();
   5:      return this.RedirectToAction("Index");
   6:  }
Potem:

   1:  public void CreateNewPosts()
   2:  {
   3:      var users = this.database.Users.ToList();
   4:      var random = users.ElementAt(Faker.NumberFaker.Number(users.Count - 1));
   5:      var dto = new NewBlogPostDto
   6:      {
   7:          Text = Faker.TextFaker.Sentences(3)
   8:      };
   9:   
  10:      SpecialAccountService.CurrentLoggedUserId = random.Id;
  11:   
  12:      this.postingService.NewBlogPost(dto);
  13:  }
Docieramy powoli do mięska. Z bazy danych wyciągamy wszystko użytkowników, losujemy jednego i zapamiętujemy. Następnie linijka 10 dzięki SpecialAccountService ustawia Id “aktualnie” zalogowanego użytkownika, a następnie zwróci to Id, gdy ktoś będzie pytać o aktualnie zalogowanego użytkownika. Na końcu znowu, wykorzystujemy wcześniej napisany serwis, który zachowuje się normalnie i jest dodatkowo testowany przez nasze generowanie testowych danych. Dlaczego SpecialAccountService działa tak soczyście?

   1:  public class SpecialAccountService : IAccountService
   2:  {
   3:      private readonly WebSecurityAccountService realAccountService;
   4:   
   5:      public SpecialAccountService()
   6:      {
   7:          this.realAccountService = new WebSecurityAccountService();
   8:      }
   9:   
  10:      public void Logout()
  11:      {
  12:          this.realAccountService.Logout();
  13:      }
  14:   
  15:      public bool Login(LoginDto dto)
  16:      {
  17:          return this.realAccountService.Login(dto);
  18:      }
  19:   
  20:      public void Register(RegisterDto dto)
  21:      {
  22:          this.realAccountService.Register(dto);
  23:      }
  24:   
  25:      static public int CurrentLoggedUserId { get; set; }
  26:   
  27:      public int GetCurrentLoggedUserId()
  28:      {
  29:          return CurrentLoggedUserId;
  30:      }
  31:  }
Klasa ta implementuje IAccountService i jest ona używana w zarejestrowana w autofacu. Natomiast gdy jest potrzeba prawdziwej rejestracji użytkownika, całą robotę deleguje do prawdziwego serwisu. Dzięki temu można rejestrować,logować czy wylogować użytkownika. A bajer z aktualnym użytkownikiem ułatwia nam tworzenie losowych wpisów dla losowych użytkowników.
Wiadomo, że na produkcje nie powinno się wypuszczać takiego kodu, dlatego normalnie trzeba by przygotować odpowiednią konfigurację IOC gdzie IAccountService będzie implementowany przez WebSecurityAccountService, a na potrzeby developerki wystarczy SpecialAccountService – można tak zrobić. Można też poprosić autofaca, aby zmienił swoje dotychczasowe zachowanie i nadpisać wcześniejsze reguły:

   1:  public class HijackService : IHijackService
   2:  {
   3:      public void RestoreIOC()
   4:      {
   5:          var builder = new ContainerBuilder();
   6:          builder.RegisterType<WebSecurityAccountService>().As<IAccountService>();
   7:          builder.Update(ApplicationIocContainer.Container);
   8:      }
   9:   
  10:      public void HijackIOC()
  11:      {
  12:          var builder = new ContainerBuilder();
  13:          builder.RegisterType<SpecialAccountService>().As<IAccountService>().SingleInstance();
  14:          builder.Update(ApplicationIocContainer.Container);
  15:      }
  16:   
  17:      public IOCStatus GetStatus()
  18:      {
  19:          var isHijacked = ApplicationIocContainer.Container.Resolve<IAccountService>() is SpecialAccountService;
  20:          return isHijacked ? IOCStatus.Hijacked : IOCStatus.Normal;
  21:      }
  22:  }
Od tej pory możemy dynamicznie rejestrować swoje klasy, które ułatwią nam udawanie kogoś kim nie jesteśmy, a po skończonej generacji danych przywrócić aplikację do oryginalnego stanu i zacząć właściwe testowanie. Największą gwiazdą tego postu jest metoda Update z IOC, która umożliwia aktualizację powiązać interfejsu z klasą implementującą bez potrzeby zmiany konfiguracji, bez potrzeby restartu aplikacji, żeby autofac skonfigurował się od nowa, etc.
A dzięki temu wszystkie możemy stworzyć sobie taki prosty interfejs aplikacji:
Gdzie istnieje możliwość normalnego działa z aplikacją, ale gdy zajdzie potrzeba, można na chwilę pożyczyć kontener, wygenerować sobie niezwykle szybko sporo danych, i ponownie jednym kliknięciem przywrócić normalny stan aplikacji. Tutaj tworzę tylko jednego użytkownika i jeden wpis, ale kto bogatemu zabronić tworzyć ich tysiące?
Uwagi, pomysły chętnie przyjmę.
Kod dostępny jest na bitbucket: https://bitbucket.org/jstadnicki/jstadnicki-blog
Zaczynam od jednego, z czasem gdy będzie więcej dużych wpisów, kolejne rzeczy związane z blogiem będę wrzucane tam.
Milej niedzieli!

Autofacu czytaj szablony z configa – please.

Czyżbym wrócił do formy z blogiem?

Niektórzy może pamiętają, a inni nie, ale walczę z takim tam sobie prywatnym projekcikiem. Otóż nastał tam etap refaktoringu i właśnie wtedy postanowiłem, że trzeba wreszcie przenieść rejestrację komponentów w IOC z kodu do configa. Używam Autofac, więc zerknełem w dokumentację i dociągnełem ‘Autofac.Configuration’ z NuGeta, jest to potrzebne aby móc grzebać z configu. Zaraz potem wziełem się do roboty,wycinek wygląda tak:
Trzeba dodać informację o nowej sekcji:

   1:  <section name="autofac" type="Autofac.Configuration.SectionHandler, Autofac.Configuration" />

A potem w sekcji autofac lecimy w taki sposób:

.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:  <autofac>
   2:      <components>
   3:          <component type="ToBeImplemented.Services.AboutService, ToBeImplemented.Services" 
   4:                     service="ToBeImplemented.Contracts.IAboutService, ToBeImplemented" />
   5:      </components>
   6:  </autofac>

type: określa jaka klasę z pełnym namespace, oraz podaje nazwę assembly
service: określa interfejs z pełnym namespace, oraz podaje nazwę assembly.
To oznacza w kodzie:

   1:  /*** ToBeImplemented project ***/
   2:  namespace ToBeImplemented.Contracts
   3:  {
   4:      public interface IAboutService
   5:      {
   6:          /* definition goes here */
   7:      }
   8:  }
   9:   
  10:  /*** ToBeImplemented.Services project ***/
  11:  namespace ToBeImplemented.Services
  12:  {
  13:      public class AboutService : IAboutService
  14:      {
  15:          /* implemenetation goes here*/
  16:      }
  17:  }

Wygląda to prosto? Mam nadzieję że tak, bo to jest proste i prosta sytuacja. Życie oczywiście nie jest takie, dlatego natrafiłem na taki problem

   1:  namespace ToBeImplemented.Services
   2:  {
   3:      using System.Collections.Generic;
   4:   
   5:      using ToBeImplemented.ViewModels;
   6:   
   7:      public class TagViewModelEqualityComparer : IEqualityComparer<TagViewModel>
   8:      {
   9:         /* code goes here */
  10:      }
  11:  }

Szablony, jak ich nie kochać? Idea jest piękna, jeden kod, wiele zastosowań. A teraz wracając do do autofaca i w zasadzie podstaw w .net i programowaniu i idei tego wpisu na blogu.
Jakiego typu jest IEqualityComparer<TagViewModel>? Hę? Bo ja się pomyliłem, zapomniałem o tym, czym naprawdę są szablony. Może sprawdzimy na takim przykładzie

   1:  using System;
   2:  using System.Collections.Generic;
   3:   
   4:  namespace ConsoleApplication1
   5:  {
   6:      class Program
   7:      {
   8:          static void Main(string[] args)
   9:          {
  10:              Console.WriteLine(typeof(IEqualityComparer<int>));
  11:              Console.WriteLine(typeof(TagViewModelEqualityComparer));
  12:          }
  13:      }
  14:   
  15:      public class TagViewModel
  16:      {
  17:          /* code goes here */
  18:      }
  19:   
  20:      public class TagViewModelEqualityComparer : IEqualityComparer<TagViewModel>
  21:      {
  22:          /* implementation goes here */
  23:      }
  24:  }

Co będzie na ekranie?

System.Collections.Generic.IEqualityComparer`1[ConsoleApplication1.TagViewModel]
ConsoleApplication1.TagViewModelEqualityComparer

Draniu! I teraz do mnie: jak mogłem o tym zapomnieć! Czyli, gdy w config kiedyś zechce zarejestrować klasę korzystającą z templejtów lub będącą templejtem to muszę pamiętać, że nie implementuje templejta, tylko jego konkretną implementacje, w moim przypadku wygląda to tak:

   1:  <component type="ToBeImplemented.Services.TagEqualityComparer, ToBeImplemented.Services"
   2:             service="System.Collections.Generic.IEqualityComparer`1[[ToBeImplemented.Model.Tag, ToBeImplemented.Model]], mscorlib" />

Od teraz będzie działać dobrze. I żeby nie było, chciałem tylko podzielić się wrażeniami z templejtów. Oczywiście autofac to dużo więcej możliwości i problemów, ale mnie ostatnio spotkał tylko ten jeden (kłamczuch ze mnie).
Po dokładne szczegóły dotyczące użytkownia autofaca zapraszam na ich strone projektu, na SO albo może ktoś mnie zapytać jeśli się wstydzi czytać i pisać po angielsku.
I to tyle. Mam nadzieję, że zaoszczędziłem dzięki temu komuś trochę czasu.