ToBeImplemented reboot

Projekty które robi się dla siebie mają pewna cechę, która jednocześnie jest czymś dobrym i złym. Taki pet-project można rozpoczynać milion razy i nikt nie robi z tego powodu afery – to plus, po milionowym rozpoczęciu szanse na zakończenie są małe – to minus.
Ja właśnie zwiększyłem cyferkę do 3, na szczęście nadal chce mi się to pisać. Rozpocząłem od nowa, bo strasznie pokomplikowałem sobie aplikację, to raz, i przy próbach prostowania doszedłem do wniosku, że chyba szybciej będzie napisać to jeszcze raz, bez takiej ilości magicznego pyłku, który użyłem wcześniej. A dwa, że nauczyłem się paru nowych rzeczy, które chciałem tutaj wykorzystać.
Tym sposobem zmierzam do warstw. O ile wcześniej dzieliłem projekt na proste części:


Jak widać, szaleństwa nie ma. Aplikacja (ASP MVC) korzysta z serwisów, serwisy z bazy oraz czasem z samych siebie (oczywiście przez interfejsy).
Ok, w zasadzie nie ma się czego czepiać, tak/nie? Mogło być lepiej, mogło być gorzej. Zamiast sześciu żółtych prostokątów, mógł być jeden. Zawsze powtarzam, że nie wolno porównywać się gorszym, pozostaje więc tylko czepiać się i szukać miejsca na poprawki.
Co skusiło mnie to tego, żeby to zmienić? W serwisach było w zasadzie wszystko co nie jest związane z bazą danych i z samym mvc, czyli: logika, mapowanie z model na view model, maile, hash, konta użytkowników, blablablabla, wszystko.
Czy to dobre? It depends. Jeśli robi się mały prosty projekt (taki książkowy), to może będzie to dobre rozwiązanie, jeśli robi się nowego fejsbuka to raczej nie, a przypominam, że już nie długo moja aplikacja zajmie jego miejsce – widzę i słyszę wasz śmiech, zobaczymy kto będzie się śmiać ostatni!

Co takiego pozmieniałem w swoim rozwiązaniu? Warstwy, więcej warstw, znacie to przysłowie, że każdy problem da się rozwiązań dodając kolejną warstwę abstrakcji?
Razem z tym zmieniłem także troszkę podejście do rozwiązywania zależności, które tworzę, wygląda mniej więcej tak:

Największe zmiany to wiele aplikacji które są wspierane, robię to żeby sprawdzić jak długo można utrzymywać wspólny kod, na razie bez cross-platform.
Idealnie jest aby aplikacje były maksymalnie lekkie, cała logika jest przeniesiona do części Bussines, która wykonuje właściwą pracę. W przypadku aplikacji WPF są jej dwa warianty, jeden oparty o Application.REST, drugi który referuje bezpośrednio do Bussines i działa „natywnie”.
Dodatkowo ważny jest brak zależności w poziomie, tzn. serwisy nie mogą zależeć od serwisów, infrastruktura od infrastruktury, czy biznes od biznesu. Poszczególne klasy w danej warstwach muszą być samodzielne, jeśli ma się pojawiać jakaś zależność, oznacza to że klasa chce wziąć na klatę zbyt dużą odpowiedzialność i że jest źle.
Prawdę mówiąc, jestem dopiero na początku tej drogi i wiem, że pojawią się bloczki (już są), które będą specyficzne dla poszczególnych aplikacji, ale jestem na to gotowy i na aktualnym stopniu mojej znajomości naszej programistycznej domeny jestem w stanie to zaakceptować.

TL;DR:
W projektach gdzie zależności są poziome, np. serwisA zależy od serwisuB są, złe. Dwa powody: może to prowadzić to zależności cyklicznych, oraz oznaczać to może, że jeden z nich ma za dużą odpowiedzialność, ponieważ część pracy przekazuje do sąsiada o którym nie powinien wiedzieć.
Słowo motywujące na dziś: nie bój się eksperymentować!

Ciekawy moich postępów czy rozwiązań, aktualny projekt możesz znaleźć tutaj:
https://jstadnicki.visualstudio.com/DefaultCollection/_git/ToBeImplemented
https://github.com/jstadnicki/tbi

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.

Dependency injection i automapper

Od dłuższego czasu korzystam z automappera, jest to genialne rozwiązanie gdy trzeba mapować jeden obiekt na drugi. Gdy w klasach, z i do której chcemy mapować wszystkie typy właściwości/pól i ich nazwy się zgadzają nie trzeba robić praktycznie nic, gdy pojawiają się inne nazwy, wystarczy tylko wskazać że pole właściwość W w klasie A to właściwość X w klasie B i tyle, raz i spokój do końca projektu. Gdy typy się nie zgadzają, można skorzystać z konwerterów, zarówno na poziomie właściwości jak i całych klas. Jeśli nie wiesz o czym mówię wróć 🙂 tutaj za kilka dnia, a ja obiecuje że popełnie wpis o automapperze. Opis – Automappera. Ale nie o tym chciałem napisać. Jeśli ktoś nie korzysta to polecam, jeśli potrzeba pomocy to proszę dać znać, a teraz do rzeczy.

Czasem konwertery aby dobrze wykonać swoją robotę potrzebują innej klasy, oczywiści można ją stworzyć w konstruktorze, ale to nie będzie ładne rozwiązanie, wszystko przez mocne wiązania, które ostatnio wyszły z mody. Na szczęście wymyślono wstrzykiwanie zależności, a automapper nie przeszkadza w skorzystaniu z tej idei. Co zrobić, aby to zadziałało?

Wystarczy taki kawałek kodu dodać do inicjalizacji automappera

.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 static void Initialize(IDependencyResolver current)
   2:  {
   3:      Mapper.Initialize(cfg => cfg.ConstructServicesUsing(t => current.GetService(t)));
   4:      // here goes other initialization of automapper          
   5:  }

Pewnie się zapytacie skąd i jak jest wołane? Ja bym się pytał. Więc tak to robię ja, w Application_Start w global.asax.cs

   1:  ModelMapper.Initialize(DependencyResolver.Current);

Potem gdy chcemy skorzystać z jakiegoś konwertera, na liście parametrów w kontruktorze można podać to czego potrzbujemy i czekać aż framework zrobic swoją magię:

   1:  public class Id2ConceptStatistics : TypeConverter<long, ConceptStatistics>
   2:  {
   3:      private readonly IUnitOfWork unitOfWork;
   4:   
   5:      public Id2ConceptStatistics(IUnitOfWork unitOfWork)
   6:      {
   7:          this.unitOfWork = unitOfWork;
   8:      }
   9:   
  10:      protected override ConceptStatistics ConvertCore(long id)
  11:      {
  12:          var conceptStatistics = this.unitOfWork.ConceptStatistics.GetByID(id);
  13:          var result = conceptStatistics ?? new ConceptStatistics();
  14:          return result;
  15:      }
  16:  }

Powyżej przykład na to jak z long (który jest id) można zmapować na pełno prawną klasę, która jest zagnieżdżona gdzieś w innej klasie.

To tyle.