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.

O braciach QueryString i ActionParameters

W .net ASP.MVC są różne mechanizmy. Są też takie, które umożliwiają zbadanie argumentów przesłanych do akcji, jak i argumentów oczekiwanych w akcji. I właśnie o nich dzisiaj. Można je wykorzystywać na dobry i zły sposób, jak każde narzędzie. Najpierw mały pokaz a potem filozofowanie. Mamy taki kod:

.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 : Controller
   2:  {
   3:      public ActionResult Index()
   4:      {
   5:          return this.View("Index");
   6:      }
   7:   
   8:      public string MustHaveString(string text)
   9:      {
  10:          return text;
  11:      }
  12:   
  13:      public string CanHaveString(string text = "")
  14:      {
  15:          return text;
  16:      }
  17:   
  18:      public string MustHaveInt(int input)
  19:      {
  20:          return input.ToString();
  21:      }
  22:   
  23:      public string CanHaveInt(int input = 0)
  24:      {
  25:          return input.ToString();
  26:      }
  27:   
  28:      public string MustHaveBoth(string text, int input)
  29:      {
  30:          string r = string.Empty;
  31:          for (int i = 0; i < input; i++)
  32:          {
  33:              r += text;
  34:          }
  35:          return r;
  36:      }
  37:   
  38:      public string CanHaveBoth(string text = "just a text", int input = 7)
  39:      {
  40:          string r = string.Empty;
  41:          for (int i = 0; i < input; i++)
  42:          {
  43:              r += text;
  44:          }
  45:          return r;
  46:      }
  47:   
  48:      public string Greed(string text, int input, int wantSoMuch, int stillNotUsing, int itAll)
  49:      {
  50:          return text + input;
  51:      }
  52:  }

Idąc od góry oczekujemy stringa, następnie oczekujemy stringa, ale mamy wartość domyślną na wypadek nie przesłania do przez użytkownika. Dalej to samo z intem, obowiązkowo oraz opcjonalnie. Potem wariacja z dwoma parametrami – obowiązkowo i opcjonalnie. I na koniec strasznie poznańska metoda, która potrzebuje wielu paramentrów, ale wykorzystuje tylko kilka z nich. Jeszcze jeszcze index, którego wyświetlenie wygląda 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:  <div class="container">
   2:      <div class="row">
   3:          <div class="col-md-6">@Html.ActionLink("Must have a string with string set", "MustHaveString", "Home", new { text = "lorem ipsum" }, new { @class = "btn btn-default" })</div>
   4:      </div>
   5:      <div class="row">
   6:          <div class="col-md-6">@Html.ActionLink("Must have a string with string not", "MustHaveString", "Home", new {}, new { @class = "btn btn-default" })</div>
   7:      </div>
   8:      <div class="row">
   9:          <div class="col-md-6">@Html.ActionLink("Can have a string with string set", "CanHaveString", "Home", new { }, new { @class = "btn btn-default" })</div>
  10:      </div>
  11:      <div class="row">
  12:          <div class="col-md-6">@Html.ActionLink("Can have a string with string not set", "CanHaveString", "Home", new {text="litwo ojczyzno moja" }, new { @class = "btn btn-default" })</div>
  13:      </div>
  14:      <hr/>
  15:      <div class="row">
  16:          <div class="col-md-6">@Html.ActionLink("Must have a int with int set", "MustHaveInt", "Home", new { input = 42 }, new { @class = "btn btn-default" })</div>
  17:      </div>
  18:      <div class="row">
  19:          <div class="col-md-6">@Html.ActionLink("Must have a int with int not", "MustHaveInt", "Home", new { }, new { @class = "btn btn-default" })</div>
  20:      </div>
  21:      <div class="row">
  22:          <div class="col-md-6">@Html.ActionLink("Can have a int with int set", "CanHaveInt", "Home", new { input=667 }, new { @class = "btn btn-default" })</div>
  23:      </div>
  24:      <div class="row">
  25:          <div class="col-md-6">@Html.ActionLink("Can have a int with int not set", "CanHaveInt", "Home", new { }, new { @class = "btn btn-default" })</div>
  26:      </div>
  27:      <hr/>
  28:      <div class="row">
  29:          <div class="col-md-6">@Html.ActionLink("Must have both set", "MustHaveBoth", "Home", new { text="plonie ogniosko", input=3}, new { @class = "btn btn-default" })</div>
  30:      </div>
  31:      <div class="row">
  32:          <div class="col-md-6">@Html.ActionLink("Must have both not", "MustHaveBoth", "Home", new { }, new { @class = "btn btn-default" })</div>
  33:      </div>
  34:      <div class="row">
  35:          <div class="col-md-6">@Html.ActionLink("Must have both set too much", "MustHaveBoth", "Home", new { text="a",input="123", x="co ja tu"}, new { @class = "btn btn-default" })</div>
  36:      </div>
  37:      <hr/>
  38:      <div class="row">
  39:          <div class="col-md-6">@Html.ActionLink("Greed only the using ones", "Greed", "Home", new { text = "a", input = "123" }, new { @class = "btn btn-default" })</div>
  40:      </div>
  41:      <div class="row">
  42:          <div class="col-md-6">@Html.ActionLink("Greed all the things", "Greed", "Home", new { text = "a", input = "123", wantSoMuch = -1, stillNotUsing = -1, itAll =-1}, new { @class = "btn btn-default" })</div>
  43:      </div>
  44:  </div>

Czyli razor w linkami do poszczególnych akcji, czasem w linku są wymagane argumenty czasem ich brak. Wygląda to tak:

Mała niespodzianka, która wyszła podczas pisania tego posta. Okazuje się, że gdy na liście argumentów jest string, ale nie zostanie przesłany i nawet jeśli nie posiada on domyślnej wartości to i tak pewne mechanizmy asp zadziałają i zostanie wywołana akcja zdefiniowana w kontrolerze. Ta sama sztuczka z int’em już nie zadziała. Zostanie wyświetlmy zółty ekran błędu, informujący o tym, że nie udało się połączyć wszystkich kropek i jest klops. Ale to nie o tym wpis – ot taka ciekawostka.

W kontrolerze możemy nadpisać wywołanie metody, np. takiej metody w taki sposób:

   1:  protected override void OnActionExecuting(ActionExecutingContext filterContext)
   2:  {
   3:      Debug.WriteLine(
   4:          string.Format("***** New Action *****n***** {0} *****", filterContext.ActionDescriptor.ActionName));
   5:      Debug.WriteLine("*** Action parameteres {0}***", filterContext.ActionParameters.Count);
   6:      filterContext.ActionParameters.ForEach(
   7:          ap => Debug.WriteLine(string.Format("Key: {0}tValue: {1}", ap.Key, ap.Value)));
   8:   
   9:      var queryString = filterContext.RequestContext.HttpContext.Request.QueryString;
  10:      Debug.WriteLine("*** Query string {0}***", queryString.AllKeys.Count());
  11:      queryString.AllKeys.ForEach(k =>
  12:          Debug.WriteLine(string.Format("Key: {0}tValue: {1}", k, queryString[k])));
  13:  }

I teraz docieramy do sedna wpisu, będziemy oglądać QueryString i ActionParameters. Dla starych wyjadaczy różnica pewnie jasna i oczywista, ja się poznałem z nimi dopiero ostatnio, oryginalni autorzy świetnie się spisali przeinaczając (jest takie słowo) ich cel: otóż, wymyślił sobie ktoś że skoro w projekcie jest bardzo dużo kontrolerów, to pewnie znajdzie się część wspólna, którą warto umieścić w jakimś bazowym dla projektu kontrolerze. Sanity check – OK. Wszystko mogło być dobrze, do momenty gdy ktoś inny, nie robiłem git blame, nie wpadł na pomysł aby sprawdzać czy argumenty się zgadzają i sprawdzać, czy action paramentes zawiera np projectId, który ma zostać wysłany od użytkownika. Sanity check – NOK. Każdy kontroler dziedziczący ten wspólny, musi mieć na liście argumentów każdej ze swojej akcji parametr “int projectId”. Co przeciwnym wypadku? Oczywiście że przekierowanie na InvalidUrl i uwaga, strona ta w żaden sposób nie informuje o tym, czego oczekiwała, ani nic. Po prostu invalid url. Sanity check – Reject!
Dlaczego mnie to tak drażni? Bo jest to ukrywanie zależności danej metody, tworzę albo pracuje z taki kodem i gdy na liście argumentów mam nie używane parametry, to je usuwam i oczekuje że kod nadal będzie działać.
Zobaczcie jak wygląda wywołanie poszczególnych linków:
***** New Action *****
***** MustHaveString *****
*** Action parameteres 1***
Key: text    Value: lorem ipsum
*** Query string 1***
Key: text    Value: lorem ipsum

***** New Action *****
***** MustHaveString *****
*** Action parameteres 1***
Key: text    Value:
*** Query string 0***

***** New Action *****
***** CanHaveString *****
*** Action parameteres 1***
Key: text    Value:
*** Query string 0***

***** New Action *****
***** CanHaveString *****
*** Action parameteres 1***
Key: text    Value: litwo ojczyzno moja
*** Query string 1***
Key: text    Value: litwo ojczyzno moja

***** New Action *****
***** MustHaveInt *****
*** Action parameteres 1***
Key: input    Value: 42
*** Query string 1***
Key: input    Value: 42

***** New Action *****
***** MustHaveInt *****
*** Action parameteres 1***
Key: input    Value:
*** Query string 0***

***** New Action *****
***** CanHaveInt *****
*** Action parameteres 1***
Key: input    Value: 667
*** Query string 1***
Key: input    Value: 667

***** New Action *****
***** CanHaveInt *****
*** Action parameteres 1***
Key: input    Value: 0
*** Query string 0***

***** New Action *****
***** MustHaveBoth *****
*** Action parameteres 2***
Key: text    Value: plonie ogniosko
Key: input    Value: 3
*** Query string 2***
Key: text    Value: plonie ogniosko
Key: input    Value: 3

***** New Action *****
***** MustHaveBoth *****
*** Action parameteres 2***
Key: text    Value:
Key: input    Value:
*** Query string 0***

***** New Action *****
***** MustHaveBoth *****
*** Action parameteres 2***
Key: text    Value: a
Key: input    Value: 123
*** Query string 3***
Key: text    Value: a
Key: input    Value: 123
Key: x    Value: co ja tu

***** New Action *****
***** Greed *****
*** Action parameteres 5***
Key: text    Value: a
Key: input    Value: 123
Key: wantSoMuch    Value:
Key: stillNotUsing    Value:
Key: itAll    Value:
*** Query string 2***
Key: text    Value: a
Key: input    Value: 123

***** New Action *****
***** Greed *****
*** Action parameteres 5***
Key: text    Value: a
Key: input    Value: 123
Key: wantSoMuch    Value: -1
Key: stillNotUsing    Value: -1
Key: itAll    Value: -1
*** Query string 5***
Key: text    Value: a
Key: input    Value: 123
Key: wantSoMuch    Value: -1
Key: stillNotUsing    Value: -1
Key: itAll    Value: -1

O ile pierwsze z nich są w porządku, bo korzystają z tego czego wymagają, o tyle mały smród na końcu, niegodziwy zachłanny gostek chce aż pięciu parametrów, podczas gdy wykorzystuje tylko dwa z pięciu. Na szczęcie HomeController nie został jeszcze dotknięty ideą sprawdzania action parameters, ale jeśli ktoś wpadnie na pomysł napisania takiego kodu:

   1:  protected override void OnActionExecuting(ActionExecutingContext filterContext)
   2:  {
   3:      if (!filterContext.ActionParameters.Keys.Contains("itAll"))
   4:      {
   5:          Response.Redirect("http://jstadnicki.blogspot.com/");
   6:      }
   7:  }

To wtedy musicie dostarczyć mieć na liście argumentów metody, uwaga napiszę to jeszcze raz: sygnatura akcji musi mieć “itAll” na liście argumentów aby w ogóle się do niej dostać. Czyli wywołanie localhost/home/index?itAll=”dzialaj” nie zadziała, ponieważ index nie ma itAll na liście parametrów, a to że został on przesłany w query string nie pomoże.
Kończąc mój długi wpis, który miał być krótki. Nie ukrywajcie zależności w kodzie, zgłaszając błąd czy to użytkownikowi czy innemu programiście, podajcie wystarczająco dużo informacji, aby od razu jasne było dlaczego coś się nie udało. I dodając kod do klasy bazowej pamiętajcie o tym, jak wielki wpływ na inne części aplikacji będzie miała wasza zmiana. Dziedziczenie nie jest rozwiązaniem. I ostatnie jeszcze ActionParameters!!!!!!!!!!!!!!!=QueryString

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.

A jak Pan klei stringi?

Człowiek idzie na rozmowę o pracę i pytają go o różne rzeczy, najczęściej pytają o to czego i tak nie będzie korzystać w tej pracy. Np, ile piłek golfowych mieści się w autobusie, czy ile jest okien w wąchocku, chociaż w takimi rzeczami lubi się parać HR. Nasi pytają o wzorce, gdzie najczęściej pada odpowiedź singleton i/lub fabryka. Wcześniej może zapytają o różnicę pomiędzy value type i reference type i kolejny klasyk to konkatenacja stringów. Każdy kto był chociaż na jednej rozmowie odpowie StringBuilder.
Jak często zdarza się wam łączyć taką ilość stringów, że tworzycie StringBuildera, żeby rzeczywiście było szybciej?. Ja chyba nigdy nie byłem (albo już nie pamiętam) w takiej sytuacji, ale zawsze mnie o to pytają. Po jednej z takich rozmów zacząłem rozmyślać co robię w takich sytuacjach, gdy mam kilka stringów do sklejenia, czy korzystam z łączenia stringów i o matko alokuje tworze ich kilka w pamięci raz na kilka sekund czy może tworze owego magicznego wszech potężnego Buildera, aby uratował mnie przy klejeniu “Ala ma 1 kota, koloru szarego“. Uświadomiłem sobie, że praktycznie zawsze korzystam ze string.Format. I co teraz? A jeśli on jest źle zaimplementowany i chłopaki z mikrosoftu kleją stringi jak przedszkolaki? Nie mogłem spać po nocy i musiałem sprawdzić. Dot peek na ratunek, sprawdziłem i jest pięknie, jestem uratowany, jest bohater, w środku siedzi StringBuilder i robi swoje. Także jeśli jesteście ciekawi co jest szybsze czy string.Format czy nowy StringBuilder to sprawdźcie sami. Ja w każdym bądź razie od teraz będę na rozmowach mówić, że korzystam ze string.Format().
Tyle, ot taki krótki wpis.

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.