Gdzie kupić hosting wordpress

„Darmowy” hosting wordpress

Gdy spotykam ludzi i rozmawiamy o różnych rzeczach, a potem coś tam o blogowaniu i samym blogu i czasami ktoś mówi: mam bloga. Postawiłem go sobie na „darmowym” hostingu wordpressa w ich domenie np. kingatest.wordpress.com to trochę się tym denerwuje. Darmowy jest może dla Was, ale ja muszę oglądać reklamy i to wciąż nie jest twój serwer.
Ale potem gdy ktoś mówi że wykupił bloga i hosting za jakieś 150 złotych to się martwię i mi smutno, że nie wiedziałem o tym wcześniej, bo mógłbym im pomóc. Jak? Continue reading

Bardzo prost logi w asp mvc

Nigdy nie ciągnęło mnie do logowania. Zawsze miałem problem z określeniem poziomu na jaki zasługiwała dana informacja, a może ostrzeżenie. Czy to błąd, czy to już krytyczny błąd. Jak mam to dobrze zalogować. Czemu te okrutne logi tak strasznie mieszają mi się w aplikacje, wtedy jeszcze nie znałem podejścia AO.
Pisanie własnego logera też nigdy nie sprawiało mi przyjemności, zawsze czułem że robię coś nie tak. Jak już poznałem nloga czy log4net i próbowałem go zaprząc do aplikacji, to konfiguracja jednego i drugiego też zawsze szła po gruzie. Summa summarum życie szło powoli, a z logów nie korzystałem i nadal nie najczęściej nie korzystam (czasem żałuje). W projektach dziedziczonych logowanie już jest i wszystko jest już ustawione i postanowione i tak ma być – a że wszyscy referują do logera, to też jest „spoko”. Ale nie o tym miałem pisać. Często się zdarza, że mi tego brakuje i wtedy albo na chwilę dodaje logowanie, a potem usuwam, a potem jak znowu potrzebuje to usuwam, albo zostawiam bo może kiedyś mi się przyda i strasznie mnie to wkurza. Dlatego gdy odkryłem tracing musiałem się tym podzielić. Otóż taram taram – ( dla części z was oczywista oczywistość), w asp.net mvc jest wbudowane proste tracowanie, które spełnia moje podstawowe wymagania i włącza się to niebotycznie prosto.

Wystarczy tyle, żeby móc przejść na stronę myWebApp/trace.axd i móc oglądać szczegóły request’ów, które otrzymuje aplikacja. Jest tam więcej przełączników, które można poznać na internecie, np. na msdn.
Oto co dostajemy za darmoszke:

Faking hel – i to praktycznie za darmo?! Oczywiście możemy dorzucać trochę swojego logowania jeśli trzeba robi się to przez taką linikę kodu:
HttpContext.Trace.Write(„Category”, „Text to log”);
Dodatkowe wpisy znajdą się w sekcji TraceInformation. Dla mnie bóstwo. Włączyć, dodać kilka linijek w kodzie na szybko, sprawdzić i wywalić.
Podobną funkcjonalność można uzyskać przy pomocy Glimpse – też wypas. Jeszcze inna opcja to F12 developers tools w każdej przeglądarce, ale tam nie dostaniemy informacji o stanie serwera. No i tyle, krótko i na temat.

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!

Powiedz nie new

Co mają wspólnego ze sobą te dwa obrazki?

Na pewno nie pasują tutaj, to raz. dwa nie są to najładniejsze obrazki, a trzy to obcisłe spodnie nie zawsze wyglądają fajnie, nawet na kobietach. On na szczęście nie ma skarpetek do sandałów.
Ale o czym dzisiaj, dzisiaj o obcisłości po angielsku w programowaniu. Słowo tight będzie jednym z bohaterów wpisu. A nawet tight coupling, czyli coś mocno wiążącego. Co tak mocno wiąże w programowaniu? Moim zdanie new jest temu winny. Wiąże bowiem ono ze sobą klienta, klasę którą korzysta z new aby zaspokoić swoje potrzeby, oraz dostarczyciela usługi, czyli klasę, która jest w stanie zaspokoić tę potrzebę.
Ale hola-hola, pomyślicie, jak to programować bez new? Przecież to hańba, skandal, herezje! Tak się nie da! Jasne, ale może uda się trochę ukrócić to panowanie new w kodzie?
Na pierwszy ogień idzie klasyczna zależność klas wyższych od niższych, w przypadku asp.net mvc przykładem jest tutaj kontroler, który może zależeć od serwisu, który posiada jakieś repozytorium danych, a to korzysta z bazy danych.
Czyli 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:  public class HomeController
   2:  {
   3:      private HomeService service;
   4:   
   5:      public HomeController()
   6:      {
   7:          service = new HomeService();
   8:      }
   9:  }
  10:   
  11:  public class HomeService
  12:  {
  13:      private HomeRepository repository;
  14:   
  15:      public HomeService()
  16:      {
  17:          repository = new HomeRepository();
  18:      }
  19:  }
  20:   
  21:  public class HomeRepository
  22:  {
  23:      private ApplicationDatabase database;
  24:   
  25:      public HomeRepository()
  26:      {
  27:          database = new ApplicationDatabase();
  28:      }
  29:  }
  30:   
  31:  public class ApplicationDatabase
  32:  {
  33:      //internalls
  34:  }

Application database to pewnie EF, nH, albo inna warstwa nad SQL, a może zestaw zapytań SQL zapisanych w stringach – nie jest to ważne w tym przykładzie.
Ten kod zadziała i będzie świetnie, ale te klasy będą działać tylko i wyłącznie ze sobą, nigdy nie pozwolą innej klasie, która może być szybsza i w ogóle – wykonać kodu, albowiem tylko HomeService, tylko HomeRepository, tylko ApplicationDatabase może to zrobić.Taki zapis powinien wam się kojarzyć z hardcodowanymi wartościami, które są tylko odrobinę lepsze niż magic numbers. Bo jaka jest różnica pomiędzy tym co napisałem powyżej a var daysToAdd=49; I tak gdy będę chciał zmieć ilośc dni, muszę przepisać i przekompilować.
A można prościej się, i uwaga prościej nie oznacza mniej kodu, prościej oznacza prościej, ewentualnie możne oznaczać lepiej:

   1:  public class HomeController
   2:  {
   3:      private IHomeService service;
   4:   
   5:      public HomeController(IHomeService homeService)
   6:      {
   7:          this.service = homeService;
   8:      }
   9:  }
  10:   
  11:  public class HomeService : IHomeService
  12:  {
  13:      private IHomeRepository repository;
  14:   
  15:      public HomeService(IHomeRepository homeRepository)
  16:      {
  17:          repository = homeRepository;
  18:      }
  19:  }
  20:   
  21:  public interface IHomeService
  22:  {
  23:  }
  24:   
  25:  public class HomeRepository : IHomeRepository
  26:  {
  27:      private IDatabase database;
  28:   
  29:      public HomeRepository(IDatabase applicationDatabase)
  30:      {
  31:          database = applicationDatabase;
  32:      }
  33:  }
  34:   
  35:  public interface IHomeRepository
  36:  {
  37:  }
  38:   
  39:  public class ApplicationDatabase : IDatabase
  40:  {
  41:      //internalls
  42:  }
  43:   
  44:  public interface IDatabase
  45:  {
  46:  }

Ponownie, nie wnikam w szczegóły implementacyjne..
Samo ApplicationDatabase się nie zmienia, zależy może od jakichś systemowych dll, ale dla nas będzie ona ostateczną granicą. Co się zmieniło? Otóż klasy teraz mówią coś takiego: „Spoko spoko ziomuś, będę działać, będzie pan zadowolony, ale potrzebuje tego i tego – inaczej mój kolega kompilator, albo runtime (wersja gdy dasz nulla) da Ci popalić – taka jest sytuacja”. W jasny sposób deklarują swoje wymogi pracy. Znika z naszego kodu słówko new, gacie przestały być takie obcisłe. Oczywiście trzeba mieć jakiś system który te klocki dobrze połączy ze sobą, ja korzystam z Autofaca, ale w zasadzie każdy konterer jest dobry. Co najważniejsze kontenery można tak ustawić, aby ich konfiguracja była w zapisana w zewnętrznych plikach, daje do możliwość zamiany zachowania aplikacji bez potrzeby jest przekompilowywania – wystarczy zmiana w konfiguracji i restart.
Inne miejsce gdzie pojawia się new to przejścia pomiędzy warstwami aplikacji, gdy model stają się dto, lub innymi dto, albo się agregują i projektują jakieś dane na i z, lub zupełnie inaczej. Tutaj z pomocą przychodzi Automapper. Całą magię przepisywania nazwy (name) z klasy A na pełną nazwę (fullname) w klasie B można oddelegować do osobnej części i ponownie powiedzieć, że do poprawnej pracy potrzebuje takich i takich rzeczy. Automapper domyślnie jest statyczną klasą, ale można ją owrapować i wstrzykiwać jako serwis, co może ułatwić ewentualne testowanie.
Ostatnie co przychodzi mi do głowy, to sytuacja gdy naprawdę muszę stworzyć obiekt, bo np. nie lubisz automapować go z kilku innych, albo jest to zlepek kilku wywołań jakichś metod albo jeszcze inaczej. Wiadomo, każdy ma wyjątkowy projekt i wyjątkowe sytuacja, które nigdy i nikomu się jeszcze nie powtórzyły. Na takie sytuację przychodzą mi do głowy dwa mechanizmy: konstruktor i/lub fabryki. Konstruktor powinien zapewnić ci to, że jeśli wszystko się udało i programista tworzący klasę, którą właśnie niułujesz (takie słowo), to po zwróceniu sterowania do twojego kodu, klasa ta będzie w stu procentach gotowa do działania. Używania inicjalizatora jest fajnym skrótem, albo gdy zostanie dołożone nowe pole do klasy, nie masz pewności, że w każdym miejscu zostanie ono poprawnie ustawione. Dodając pole do klasy i na listę parametrów konstruktora masz pewność, że kompilator ci tego nie zapomni wytknąć. Jest to jedno rozwiązanie, które nie zmniejsza new w kodzie, ale nie zmniejsza ciasnowatości (znowu takie słowo) spodni. Lepszym podejściem są fabryki, które wezmą tę robotę na siebie. Znowu rozwiązanie, które spowoduje ze kodu nie będzie mniej, ale umożliwi napisanie kodu który nie musi być mocno związany z typem, fabryki mogą zwracać obiekty przez interfejsy, znowu więcej kody, za luźne gacie. Dodatkowo obowiązek posiadania wiedzy o tworzeniu klas wynikowych zostanie oddelegowany do fabryk. Fabryki możecie podawać jako wasze ulubione wzorce projektowe na rozmowach o pracę
Jeśli macie inne pomysły na to jak pozbyć się new, chętnie je poznam. Jednocześnie możecie się zastanawiać czemu to robić? Po pierwsze w luźnych spodniach lepiej się chodzi, ale luźne tak bez przesady. Po drugie, moim zdaniem kod staje prostszy w utrzymaniu. Trudniej w takim kodzie o wycieki, tak w .net można zrobić wycieki pamięci. Po kolejne, stajecie się bardziej pro. Kod jest otwarty na modyfikacje i rozszerzenia, ułatwia testowanie, jest ładny i miły w dotyku. Jeśli tego nie czujecie, to nic na siłę. Ja po prostu musiałem to napisać – jeśli mogę unikam new.
ps. A miał to być krótki wpis.

Własna konfiguracja w app.config

Jeśli to czytacie, to znaczy że zdążyłem jeszcze z wpisem przed nowym rokiem.  Udało mi się napisać jeszcze jednego posta przed nowym rokiem – jestem królem świata!

Dzisiaj o wykorzystaniu app.config dla własnych celów. Skąd taki pomysł? Otóż jak każdy pewnie ma lub miał w życiu, czasem mieć taką potrzebę aby zmieniać zachowanie aplikacji inaczej niż przez jej przekompilowanie. Wtedy właśnie z pomocą przychodzą pliki .txt, .xml, .config czy dawno dawno temu .ini. Można napisać własne system do obsługi konfiguracji, własne klasy serializujące i deserializujące, można pisać cudne rzeczy. Ale po co, skoro można skorzystać z tego co oferuje app.config. Też tak pomyślałem i rozpętało się piekło. Bo nie da się ot tak dowalić do app.configu swojego kawałka xmla. Trzba to wszystko dokładnie, ale to dokładnie opisać. O ile wytrzymacie ten wpis, to zaraz wszystko wyjaśnię.

Zacznę od początku na prostych obiektach. Np. gdybym chciał w pliku zdefiniować wygląd jakiegoś prostego obiektu, np.

.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:  <?xml version="1.0" encoding="utf-8" ?>
   2:  <configuration>
   3:      <MyConfiguration>
   4:          <Rectangle Color="Red" Width="10" Height="5" Fill="False"/>
   5:      </MyConfiguration>
   6:  </configuration>

Wiecie co się stanie przy próbie uruchomienia takiego prostego kodu jak ten:

.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:  static void Main(string[] args)
   2:  {
   3:      var cfg = ConfigurationManager.AppSettings;
   4:  }

Aby konfiguracja zadziałała, trzeba dodać informację do app.cofig o naszej sekcji konfiguracyjnej.

   1:  <configSections>
   2:      <section name="MyConfiguration" type="AppConfigExample.MyConfiguration, AppConfigExample"/>
   3:  </configSections>

W części type podajemy gdzie znajdzie się klasa opisująca naszą konfigurację. Wpis musi zawierać pełny namespace, następnie należy podać także assembly w który się ona znajduje. Tutaj wszystko mam w jednym assembly.
Skoro trzeba podać klasę to trzeba ją także gdzieś i jakoś zdefiniować i podziedziczyć:

   1:  public class MyConfiguration : ConfigurationSection
   2:  {
   3:  }

A następnie spróbować się do niej dostać:

   1:  var cfg = ConfigurationManager.GetSection("MyConfiguration");

Niestety to nadal za mało (powoli buduje nastrój grozy):

Lubię .net i jego wyjątki za czytelne błędy i opisy problemów, lubię też google czy SO za pomoc w rozwiązywaniu tych wyjątków. Chwila szukania i już wiem co należy dodać, aby móc odczytać mój kwadracik z konfiguracji:

   1:  public class MyConfiguration : ConfigurationSection
   2:  {
   3:      [ConfigurationProperty("Rectangle")]
   4:      public Rectangle Rectangle
   5:      {
   6:          get { return ((Rectangle)(base["Rectangle"])); }
   7:      }
   8:  }

Skoro wspominamy tutaj o klasie Rectangle to należy ją także jakoś zdefiniować i znowu dziedziczenie:

   1:  public class Rectangle: ConfigurationElement
   2:  {
   3:      [ConfigurationProperty("Color")]
   4:      public ConsoleColor Color
   5:      {
   6:          get { return (ConsoleColor)base["Color"]; }
   7:          set { base["Color"] = value; }
   8:      }
   9:   
  10:      [ConfigurationProperty("Width")]
  11:      public int Width
  12:      {
  13:          get { return (int)base["Width"]; }
  14:          set { base["Width"] = value; }
  15:      }
  16:   
  17:      [ConfigurationProperty("Height")]
  18:      public int Height
  19:      {
  20:          get { return (int)base["Height"]; }
  21:          set { base["Height"] = value; }
  22:      }
  23:   
  24:      [ConfigurationProperty("Fill")]
  25:      public bool Fill
  26:      {
  27:          get { return (bool)base["Fill"]; }
  28:          set { base["Fill"] = value; }
  29:      }
  30:  }

Gdy mamy taki kod możemy spokojnie spróbować swoich sił i uruchomić coś takiego:

   1:  var cfg = ConfigurationManager.GetSection("MyConfiguration") as MyConfiguration;
   2:   
   3:  var oc = Console.ForegroundColor;
   4:  Console.ForegroundColor = cfg.Rectangle.Color;
   5:   
   6:  Console.WriteLine("Width:{0}, Height:{1}, Filled:{2}", cfg.Rectangle.Width, cfg.Rectangle.Height,
   7:                    cfg.Rectangle.Fill);
   8:   
   9:  Console.ForegroundColor = oc;
Co powinno dać taki wynik:

Chyba nie macie dosyć? Nie warto zaprzestawać na tylko jednym takim obiekcie, skoro na świecie jest tyle pięknych prostokątów, załóżmy taki przypadek:

   1:  <MyConfiguration>
   2:      <Rectangle Color="Red" Width="10" Height="5" Fill="False"/>
   3:      <Rectangle Color="Green" Width="12" Height="6" Fill="False"/>
   4:      <Rectangle Color="Black" Width="14" Height="3" Fill="True"/>
   5:      <Rectangle Color="White" Width="15" Height="2" Fill="True"/>
   6:      <Rectangle Color="Gray" Width="16" Height="10" Fill="False"/>
   7:  </MyConfiguration>

Co się stanie po uruchomieniu aplikacji z taką konfiguracją?

No racja racja, przecież to kolekcja prostokątów, powinna wyglądać inaczej, bo skąd biedny komputerek może wiedzieć o który obiekt dokładnie nam chodzi.

Macie tak czasami, że wy wiecie że się nie uda czegoś zrobić, a komputer jeszcze walczy? Np. klikniecie przez przypadek na napędzie CD, tam nie ma płyty, wszyscy to wiedzą, ale nie, 15 sekund trzeba czekać zanim komputerek też się dowie. Sieć nie działa, wszyscy wiedzą, a komp walczy. Najgorsze jest to że się nie uczy. Skoro nie było płyty, i napęd się nie wysunął i nie wsunął, to płyty nadal tam nie będzie. Ale kliknij znowu na ikonę z CD i znowu czekasz, bo może jednak jest. Ech takie szybkie i takie głupie.

Wracając do naszego problemu, jak zrobić żeby ten biedny komputerek poradził sobie z wieloma prostokącikami:

   1:  <Rectangles>
   2:      <Rectangle Color="Red" Width="10" Height="5" Fill="False"/>
   3:      <Rectangle Color="Green" Width="12" Height="6" Fill="False"/>
   4:      <Rectangle Color="Black" Width="14" Height="3" Fill="True"/>
   5:      <Rectangle Color="White" Width="15" Height="2" Fill="True"/>
   6:      <Rectangle Color="Gray" Width="16" Height="10" Fill="False"/>
   7:  </Rectangles>

Ale wtedy znowu będzie marudzić, że unrecognized element i że „Rectangles” to ten unrecognized. Trzeba więc zaktualizować informację z klasie MyConfiguration na taki:

   1:  public class MyConfiguration : ConfigurationSection
   2:  {
   3:      [ConfigurationProperty("Rectangles")]
   4:      public Rectangles Rectangles
   5:      {
   6:          get { return ((Rectangles)(base["Rectangles"])); }
   7:      }
   8:  }

Oraz zdefiniować odpowiednio klasę Rectangles. Możecie spróbować samodzielnie ją zdefiniować i zobaczyć jaki poleci wyjątek, lub też przeczytać dalej i zobaczyć jak robią to inni:

   1:  [ConfigurationCollection(typeof(Rectangle))]
   2:  public class Rectangles : ConfigurationElementCollection
   3:  {
   4:      protected override ConfigurationElement CreateNewElement()
   5:      {
   6:          return new Rectangle();
   7:      }
   8:   
   9:      protected override object GetElementKey(ConfigurationElement element)
  10:      {
  11:          return element.GetHashCode();
  12:      }
  13:  }

Linijka 11 nie wygląda najlepiej. A poza tym może spowodować błąd (podczas domyślnej implementacji GetHashCode), aby go uniknąć należy napisać własne GetHashCode w klasie Rectangle lub dołożyć jakiś klucz, co później ułatwi dostęp do wybranych elementów z kolekcji.
Klasę Rectangle należy uaktualnić o następujące pole:

   1:  [ConfigurationProperty("Id", IsKey = true, IsRequired = true)]
   2:  public int Id
   3:  {
   4:      get { return (int)base["Id"]; }
   5:      set { base["Id"] = value; }
   6:  }

Natomiast metoda GetElementKey wyglądać teraz będzie tak:

   1:  protected override object GetElementKey(ConfigurationElement element)
   2:  {
   3:      return ((Rectangle)element).Name;
   4:  }

Kolekcja elementów w app.config wygląda teraz tak:

   1:  <Rectangles>
   2:      <add Id="1" Color="Red" Width="10" Height="5" Fill="False"/>
   3:      <add Id="2" Color="Green" Width="12" Height="6" Fill="False"/>
   4:      <add Id="3" Color="Black" Width="14" Height="3" Fill="True"/>
   5:      <add Id="4" Color="White" Width="15" Height="2" Fill="True"/>
   6:      <add Id="5" Color="Gray" Width="16" Height="10" Fill="False"/>
   7:  </Rectangles>

I znowu coś nie cool, to idiotyczne add – bleh, brzydal. Gdzie się podziało ładne Rectangle? Ponownie zachęcam do sprawdzenia wyjątku jaki leci, gdy zamiast add będzie Rectangle. Na szczęście i to da się napisać lepiej, wystarczy ponownie zaktualizować informację o klasie Rectangles, brakuje tam małej informacji w atrybutach klasy:

   1:  [ConfigurationCollection(typeof(Rectangle), AddItemName = "Rectangle")]
   2:  public class Rectangles : ConfigurationElementCollection { /* unicorns here */ }

Od teraz znowu zamiast add możemy stosować Rectangle i uruchomić taki kod:

   1:  var cfg = ConfigurationManager.GetSection("MyConfiguration") as MyConfiguration;
   2:  var oc = Console.ForegroundColor;
   3:   
   4:  foreach (Rectangle rectangle in cfg.Rectangles)
   5:  {
   6:      Console.ForegroundColor = rectangle.Color;
   7:      Console.WriteLine("Width:{0}, Height:{1}, Filled:{2}", rectangle.Width, rectangle.Height,rectangle.Fill);
   8:      Console.ForegroundColor = oc;
   9:  }

Co według mojego działania da taki wynik:

A ja wybrać ten jeden w swoim rodzaju Rectangl? Skoro mamy jego Id to powinniśmy z niego jakoś skorzystać. Może linq? Potrzebujemy tylko IEnumerable w klasie Rectangles i będzie działać, a więc:

   1:  [ConfigurationCollection(typeof(Rectangle), AddItemName = "Rectangle")]
   2:  public class Rectangles: ConfigurationElementCollection, IEnumerable<Rectangle>
   3:  {
   4:      { /* dragons here */ }
   5:      public new IEnumerator<Rectangle> GetEnumerator()
   6:      {
   7:          int count = base.Count;
   8:          for (int i = 0; i < count; i++)
   9:          {
  10:              yield return base.BaseGet(i) as Rectangle;
  11:          }
  12:      }
  13:  }

I można już napisać coś takiego w kodzie:

   1:  Rectangle rect = cfg.Rectangles.Single(r => r.Id == 2);
   2:   
   3:  Console.ForegroundColor = rect.Color;
   4:  Console.WriteLine("Width:{0}, Height:{1}, Filled:{2}", rect.Width, rect.Height, rect.Fill);

Od teraz wszyscy mogą zazdrościć wam wiedzy o tym, jak lepiej wykorzystywać app.config w swoich aplikacjach.

A teraz cały kod z wpisu:

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Configuration;
   4:  using System.Linq;
   5:  using System.Text;
   6:   
   7:  namespace AppConfigExample
   8:  {
   9:      class Program
  10:      {
  11:          static void Main(string[] args)
  12:          {
  13:              var cfg = ConfigurationManager.GetSection("MyConfiguration") as MyConfiguration;
  14:              var oc = Console.ForegroundColor;
  15:   
  16:              foreach (Rectangle rectangle in cfg.Rectangles)
  17:              {
  18:                  Console.ForegroundColor = rectangle.Color;
  19:                  Console.WriteLine("Width:{0}, Height:{1}, Filled:{2}", rectangle.Width, rectangle.Height, rectangle.Fill);
  20:              }
  21:   
  22:              Console.WriteLine("****************************************");
  23:              Rectangle rect = cfg.Rectangles.Single(r => r.Id == 2);
  24:   
  25:              Console.ForegroundColor = rect.Color;
  26:              Console.WriteLine("Width:{0}, Height:{1}, Filled:{2}", rect.Width, rect.Height, rect.Fill);
  27:   
  28:   
  29:   
  30:              Console.ForegroundColor = oc;
  31:          }
  32:      }
  33:   
  34:   
  35:      public class MyConfiguration : ConfigurationSection
  36:      {
  37:          [ConfigurationProperty("Rectangles")]
  38:          public Rectangles Rectangles
  39:          {
  40:              get { return ((Rectangles)(base["Rectangles"])); }
  41:          }
  42:      }
  43:   
  44:      [ConfigurationCollection(typeof(Rectangle), AddItemName = "Rectangle")]
  45:      public class Rectangles : ConfigurationElementCollection, IEnumerable<Rectangle>
  46:      {
  47:          protected override ConfigurationElement CreateNewElement()
  48:          {
  49:              return new Rectangle();
  50:          }
  51:   
  52:          protected override object GetElementKey(ConfigurationElement element)
  53:          {
  54:              return ((Rectangle)element).Id;
  55:          }
  56:   
  57:          public new IEnumerator<Rectangle> GetEnumerator()
  58:          {
  59:              int count = base.Count;
  60:              for (int i = 0; i < count; i++)
  61:              {
  62:                  yield return base.BaseGet(i) as Rectangle;
  63:              }
  64:          }
  65:      }
  66:   
  67:      public class Rectangle : ConfigurationElement
  68:      {
  69:          [ConfigurationProperty("Id", IsKey = true, IsRequired = true)]
  70:          public int Id
  71:          {
  72:              get { return (int)base["Id"]; }
  73:              set { base["Id"] = value; }
  74:          }
  75:   
  76:          [ConfigurationProperty("Color")]
  77:          public ConsoleColor Color
  78:          {
  79:              get { return (ConsoleColor)base["Color"]; }
  80:              set { base["Color"] = value; }
  81:          }
  82:   
  83:          [ConfigurationProperty("Width")]
  84:          public int Width
  85:          {
  86:              get { return (int)base["Width"]; }
  87:              set { base["Width"] = value; }
  88:          }
  89:   
  90:          [ConfigurationProperty("Height")]
  91:          public int Height
  92:          {
  93:              get { return (int)base["Height"]; }
  94:              set { base["Height"] = value; }
  95:          }
  96:   
  97:          [ConfigurationProperty("Fill")]
  98:          public bool Fill
  99:          {
 100:              get { return (bool)base["Fill"]; }
 101:              set { base["Fill"] = value; }
 102:          }
 103:      }
 104:  }

Oraz app.config:

   1:  <?xml version="1.0" encoding="utf-8" ?>
   2:  <configuration>
   3:      <configSections>
   4:          <section name="MyConfiguration" type="AppConfigExample.MyConfiguration, AppConfigExample"/>
   5:      </configSections>
   6:      <MyConfiguration>
   7:          <Rectangles>
   8:              <Rectangle Id="1" Color="Red" Width="10" Height="5" Fill="False"/>
   9:              <Rectangle Id="2" Color="Green" Width="12" Height="6" Fill="False"/>
  10:              <Rectangle Id="3" Color="Black" Width="14" Height="3" Fill="True"/>
  11:              <Rectangle Id="4" Color="White" Width="15" Height="2" Fill="True"/>
  12:              <Rectangle Id="5" Color="Gray" Width="16" Height="10" Fill="False"/>
  13:          </Rectangles>
  14:      </MyConfiguration>
  15:  </configuration>

To by było na tyle, bierzcie i korzystajcie z tego, bo to dobre jest.
Pozostaje mi tylko życzyć dobrego w nadchodzącym nowym roku!
Tej!