Category Archives: Ogólne

HTTP 2.0 Multiplexing, HTTP 1.1 Pipelining oraz HOL blocking

Kilka postów chcę przeznaczyć na temat HTTP 2.0. Zamiast pisać tylko o nowościach w nowej wersji protokołu, warto najpierw zrozumieć jak działała dotychczasowa wersja czyli HTTP 1.1.

Pierwszym problemem HTTP 1.0 oraz HTTP 1.1 była obsługa wielkiej ilości zapytań. Każda strona, aby prawidłowo wyświetlić się potrzebuje załadować mnóstwo zewnętrznych zasobów takich jak skrypty JS, arkusze css czy grafika. Ponadto HTTP oparty jest na TCP więc ponadto mamy do czynienia z TCP handshake zanim połączenie jest zainicjalizowane.  Połączenia internetowe są coraz lepsze, ale szybkość załadowania stron nie zawsze jest satysfakcjonująca. Wyobraźmy sobie ekstremalną sytuację. Strona posiada 10 skryptów JS, 2 arkusze CS oraz 10 plików graficznych. Zasoby w HTTP nie są przesyłane razem. Najpierw ściągany jest plik HTML, a potem każdy zasób zewnętrzny jest ściągany jeden po drugim. Po mimo szybkiej, średniej szybkości łącza internetowego, opóźnienie (latency) wiążące się z wysoká liczbą zapytań sprawia, że strona działa wolno.

W HTTP 1.0 wszystkie zapytania musiałby być wysyłane pojedynczo (szeregowo). Jeśli mamy zatem 20 zapytań, najpierw musieliśmy wysłać pierwsze, czekać na odpowiedź i dopiero potem wysłać drugie. W przypadku internetu satelitarnego czy mobilnego, strony po prostu wczytywały się bardzo wolno, mimo wysokiej średniej szybkości transmisji.

W odpowiedzi na oczywisty problem, w HTTP 1.1 wprowadzono HTTP pipelining. Dzięki temu, możliwe było wysyłanie kilku zapytań w tym samym połączeniu TCP. Korzyści były ogromne. Po pierwsze tylko jeden TCP Handshake. Zamiast czekać na odpowiedź mogliśmy wysłać najpierw wszystkie zapytania, a potem odbierać odpowiedzi jedno po drugim. Niestety musiało to odbywać się na zasadzie FIFO – odpowiedzi musiały przychodzić w tej samej kolejności co wysłane zapytania. Stanowiło to tak ogromne ograniczenie, że większość przeglądarek nie korzysta dziś nawet z HTTP pipelining.

Podstawowy problem to Head Of Line Blocking. Wiemy, że odpowiedzi muszą przychodzić w tej samej kolejności co zapytania. Co jeśli drugie zapytanie trwa bardzo długo? Skutek będzie taki, że będzie to blokowało kolejne odpowiedzi i przyszłe zapytania. Jeśli zatem wszystkie zapytania byłyby wysyłane za pomocą HTTP Pipeline efekt mógłby być odwrotny do zamierzonego – jeśli pierwszy element zajmuję bardzo długo, wtedy kolejne muszą czekać. Co jeśli ten pierwszy element tak naprawdę nie jest niezbędny do wyświetlenia większości zawartości na stronie? HTTP pipelining zniwelował problemy związane z opóźnieniem, ale ryzyko blokady HOB odstraszało wielu twórców przeglądarek od adaptacji tego.

I tutaj przychodzimy do HTTP 2.0 Multiplexing. Protokół HTTP 2.0 jest binarny, w przeciwieństwie do wersji poprzednich. Wysyłane pakiety stanowią ramki składające się z nagłówku i ciała. Wprowadzono definicje strumieni oraz priorytetów.  Dane wysyłane w obrębie dwóch różnych strumieni są niezależne od siebie, a co za tym idzie, mogą się wzajemnie przeplatać.  Innymi słowy, HTTP 2.0 multiplexing to prawidłowa implementacja wysyłania wielu zapaytań w obrębie tego samego połączenia TCP. Odpowiedzi już nie muszą być wysyłane w tej samej kolejności przez serwer i zostanie to prawidłowo obsłużone. Wspomniana kolejka FIFO nie jest już potrzebna. Ustawienia priorytetów umożliwia uzyskanie odpowiedzi w dowolnej kolejności. Serwer po utrzymaniu wielu zapytań naraz może skorzystać z priorytetów, aby przetwarzać je w odpowiedniej kolejności.

HTTP Multiplexing stanowi jedno z podstawowych usprawnień HTTP 2.0, który wpływa bardzo korzystnie na wydajność stron internetowych, szczególnie w środowisku z wysokim opóźnieniem ( połączenie mobilne). W przyszłych wpisach zajmiemy się kolejnymi usprawnieniami. Dobrą wiadomością jest, że jako programiści nie musimy wiele robić. HTTP 2.0 został zaprojektowany tak, aby mógł zostać wprowadzony bez konieczności aktualizacji istniejącego kodu. Wszystkie pryncypalne zasady takie jak wysyłanie ciasteczek, nagłówki, sesje itp mają nadal rację bytu – HTTP 2.0 to nie zmiana architektury.

LINQPad – przydatne narzędzie w eksperymentowaniu z C#\LINQ

Wiele osób pewnie już od dawna korzysta LINQPad. Osobiście nigdy nie miałem okazji korzystać z tego narzędzia i zawsze myślałem, że w przypadku TDD jest ono po prostu zbędne.

Ostatnio jednak zainstalowałem z ciekawości i okazało się bardzo przydatne w przypadku testowania krótkich snippet’ów. Czasami chcę sprawdzić tylko np. formatowanie daty i wtedy zwykle uruchamiałem osobną instancję Visual Studio,  immediate windows w VS albo po prostu pisałem konkretny test. Nie zawsze jednak jest to wygodne i praktyczne.

LINQPad to taki notatnik dla programisty. Nazwa może być myląca bo nie chodzi tutaj tylko o wyrażenia LINQ. Możemy testować jakikolwiek kod C#. Często oczywiście jest to po prostu LINQ. Ostatnio zastanawiałem się, czy Take(10) na tablicy 3-elementowej wyrzuci wyjątek. Za pomocą LINQPad napisałem:

1

 

Wszystko stało się jasne bardzo szybko – wizualizacja danych jest bardzo przejrzysta.

Często na blogu poruszałem tematy optymalizacji i analizy kodu IL. W LINQPad możemy również obejrzeć IL:

1

 

Narzędzie oczywiście nie służy do pisania kodu, ale sprawdzania różnych operacji. Nierzadko zdarza się, że chcemy sprawdzić np. jak String.Split działa, a za pomocą LINQPad bardzo szybko zobaczymy wynik operacji.

Ponadto, LINQPad jest przydatny w analizie zapytań SQL wygenerowanych przez narzędzia ORM.  Na przykład, po kliknięciu w “Add Connection” możemy wybrać konkretny sterownik:

1

 

Następnie po wykonaniu zapytania, dostaniemy oczywiście dane:

1

 

Przełączając się do zakładki SQL, zobaczymy wygenerowane zapytanie SQL:

1

GIT: Kiedy używać Rebase oraz Merge

Jakiś czas temu, pisałem o różnicach między Rebase oraz Merge. Dzisiaj chciałbym rozważyć kilka scenariuszy, gdzie można stosować Rebase.

Przede wszystkim jeśli różnica nie jest jasna, wtedy zawsze bezpieczniejszą opcją jest Merge. Nie ma w tym nic złego i najwyżej historia logów będzie mniej czytelna, ale  przynajmniej uniknie się niepotrzebnych problemów.

Zacznijmy od scenariusza kiedy nie używać rebase. W skrócie nie należy stosować rebase na publicznych gałęziach, gdzie nadpisane zmiany, mogą być u kogoś już w lokalnym repozytorium. Rozważmy diagram z poprzedniego wpisu. Po zmianach w feature branch, uzyskaliśmy następującą strukturę:

image

 

Po dokonaniu rebase na master, zmiany 4 oraz 5 zostaną przepisane:

image

Widzimy, że zostały one przesunięte. Techniczne będą miały zupełnie inny hash code. Problem jest taki, że cześć użytkowników mogła już pobrać kod przed dokonaniem rebase. Nie trudno domyślić się, że w momencie kiedy będą chcieli wykonać GIT PUSH, nastąpią problemy ponieważ commity 4 i 5 w ich wersji będą inne niż te na serwerze, ponieważ zostały one przepisane za pomocą Rebase. Innymi słowy zmiany 4  i 5 są inne niż te co mają pozostali użytkownicy. Skoro gałąź jest publiczna może to utrudnić życie wielu użytkownikom.

Wniosek taki, że jeśli chcemy przenieść zmiany z feature branch do master, która jest publiczną gałęzią należy używać komendy MERGE.

Rozważmy inny teraz kierunek, a mianowicie master->feature branch. Załóżmy, że tworzymy gałąź, z której nikt dalej nie będzie “branchować” ponownie. Innymi słowy zmiany w feature branch będą zsynchronizowane wyłącznie z master. W takim przypadku, użycie rebase, aby pobrać zmiany z master  jest bezpieczne i dzięki niemu mamy prostą i liniową historie zmian. Oczywiście znów zostaną nadpisane commity, ale będą to wyłącznie nasze zmiany, które nie są jeszcze dostępne w publicznej gałęzi. Nie ma w tym przypadku znaczenia to, że hash code zostanie zmodyfikowany. W sytuacji jednak, gdy kilka osób pracuję nad tym samym feature branch, wtedy napotkamy identyczne problemy jak na powyższym rysunku. Oznacza to, że wtedy należy korzystać z merge. Innymi słowy, gałąź publiczna + rozwidlenia wykluczają użycie rebase. Często jednak feature branch ma charakter bardziej prywatny i wtedy nic nie stoi na przeszkodzie, aby cieszyć się prostą historią zmian wykonaną przez rebase.

Kolejny przykład to współdzielenie tej samej gałęzi. Czasami programiści pracują po prostu na master, ewentualnie osobnej gałęzi. W tym przypadku również można rozważyć rebase i jest to bezpieczna opcja. Dlaczego? W końcu pracujemy na publicznej gałęzi, a jak wiemy już rebase+publiczna gałąź zwykle jest bardzo złym pomysłem. W tym przypadku jednak, będziemy nadpisywać wyłącznie swoje własne commity, które nie są jeszcze publiczne.  Jedyną przeszkodzą może być sytuacja, w które współdzielimy nasz commit za pomocą np. plików zmian. Jeśli to zrobimy, wtedy w przypadku rebase zostaną one przepisane i ta osoba, która otrzymała wcześniej od nas zmiany (np. poprzez email) będzie miała inne zmiany niż te po dokonaniu REBASE. Jeszcze raz podkreślam mowa tutaj o publicznej, współdzielonej gałęzi, która jednak nie ma żadnych rozwidleń. Wtedy mamy pewność, że dokonując rebase możemy jedynie przepisać własne, lokalne zmiany.

Podsumowując:

1. Feature branch –> master:  merge

2. Master->Feature branch (w celu synchronizacji) –> rebase jest w porządku o ile, ktoś nie korzysta ze wspomnianej gałęzi np. poprzez dalsze rozwidlanie.  W przeciwnym wypadku zalecany jest merge ponieważ mogą nastąpić podobne problemy jak w punkcie 1.

3. Gałęzie współdzielone (brak rozwidleń):  rebase

GIT: Merge vs Rebase

Coraz więcej osób korzysta dzisiaj z GIT, zamiast starych i scentralizowanych repozytoriów. Czasami jednak mnogość komend może być przytłaczająca, zwłaszcza na początku. Dzisiaj chciałbym wyjaśnić czym różni się Merge od Rebase.

Obie komendy służą do tego samego czyli synchronizacji dwóch gałęzi kodu np. lokalnego repozytorium ze zdalną gałęzią. Efekt na końcu będzie “taki sam”, czyli połączenie lokalnego kodu, ze zmianami naniesionymi np. w gałęzi. Dlaczego zatem GIT tak to “komplikuje”? W TFS czy w SVN zwykle korzystało się z komendy typu Update i to wszystko. W GIT jest trochę inaczej i zwykle korzysta się np. z Fetch+Merge, Fetch+Rebase lub po prostu PULL.

Najlepiej pokazać to na przykładzie. Załóżmy, że mamy pewną gałąź, np. master:

image

Powyższy rysunek przedstawia 3 zmiany (commit). Następnie użytkownik tworzy gałąź, w celu zaimplementowania czegoś (feature branch) i dokonuje również kilku zmian:

image

Bardzo możliwe, że w tym samym czasie ktoś zmodyfikował master:

image

Po jakimś czasie będziemy musieli zintegrować Branch A z master. Mamy do wyboru Merge albo Rebase.

Jeśli użyjemy klasyczny merge (branch->master), wtedy struktura będzie wyglądać następująco:

image

Komenda tworzy dodatkowy commit, który scala dwie gałęzie. W historii zmian, zobaczymy coś w stylu “merge commit”, który scala je. Powstanie zatem rozwidlienie i historia zawartości nie będzie liniowa jak to widać na powyższym rysunku. Zaletą merge jest prostota bo nie modyfikujemy poprzedniego kodu – po prostu tworzymy na samej górze dodatkowy commit, który zakańcza rozwidlenie gałęzi. Wadą rozwiązania jest fakt, że przy każdym merge, powstaje dodatkowy commit, co zwykle utrudnia przeglądanie historii.

Rebase z kolei, spowoduje, że będziemy mieli  bardzo przejrzystą historie zmian:

image

Widzimy, że struktura jest liniowa. Rebase jak sama nazwa sugeruje, polega na zamianie podstawy.  W tym przypadku, commity’ z gałęzi “branch a” zostały przepisane u podstawy master. Efekt jest taki, że nie musimy tworzyć sztucznego “merge commit” i w historii zmian widzimy tylko to, co faktycznie zostało zmodyfikowane przez innych użytkowników.

Innymi słowy, Rebase tworzy liniową strukturę, ale modyfikuje historie zmian, z kolei Merge tworzy dodatkowy commit, scalający rozwidlenia, ale powoduje to powstanie nieliniowej struktury zmian.

Struktura liniowa nie jest jednak zawsze pożądana – w końcu rozwidlenie w przypadku Merge dostarcza jakąś informację o kontekście zmian. W przypadku Rebase nie wiadomo, że feature branch został scalony i stąd nastąpiły zmiany. Ponadto, ze względu, że rebase dokonuje przepisania commitów może być to czasami niebezpieczne – w końcu jest to ingerencja w już zapisany kod.

W przyszłym poście postaram się napisać kilka wskazówek dotyczących wyboru między Rebase a Merge oraz czego należy unikać (oczywiście z mojego punktu widzenia).

Dokumentacja REST API – Swagger

Dzisiaj chciałbym pokazać Swagger, doskonałe narzędzie służące do dokumentacji REST API.

Swagger, korzysta z plików JSON, którymi można opisać nasze API. W przypadku RESTful api m.in. możemy określić:

  1. Nazwę zasobu
  2. Typ zwracanych danych
  3. Opis obiektów
  4. Wersję API
  5. Zwracane kody statusu HTTP
  6. Parametry (np. query lub HTTP body)
  7. HTTP verbs (PUT, GET, POST etc)

Gotowy, przykładowy plik opisujący API możemy znaleźć tutaj:

http://petstore.swagger.io/v2/swagger.json

Oczywiście sam plik nie jest zbyt łatwy w odczycie dlatego zwykle korzysta się z Swagger-UI. Jest to prosta aplikacja internetowa, która wizualizuje wspomniany plik JSON. Dzięki niej możemy odczytywać dokumentację, jak i testować różne zapytania. Przykład:

http://petstore.swagger.io

Screenshot:

image

Pozostaje wyjaśnić jak stworzyć wspomniane pliki JSON. Ręczne edytowanie w pliku tekstowego jest dość mozolne.  Do dyspozycji mamy edytor online:

http://editor.swagger.io/#/

Screenshot:

image

Modyfikując plik, na bieżąco widać po prawej strony rezultat (podgląd).

Plik json opisujący API powinien być hostowany razem z usługą. Z kolei w przypadku Swagger-UI  jest to niekonieczne. Można np. mieć jedno repozytorium na całą firmę i tam trzymać swagger-ui, który wskazuje do poszczególnych usług. Inna możliwość to trzymanie swagger-ui razem z usługą i JSON.

Istnieje wiele frameworków, które integrują  Swagger z konkretną technologią np. ASP.NET MVC WebAPI. Wtedy framework wygeneruje JSON na podstawie np. komentarzy i analizy kodu. Bardzo wygodne i dzięki temu unikniemy rozbieżności między dokumentacją, a faktycznym stanem usługi.

Niestety nie znalazłem (na dziej dzisiejszy) dobrego narzędzia dla Nancy. Istnieje pewien projekt (wciąż w wersji PreAlph), ale nie jest zbyt stabilny:

https://github.com/khellang/Nancy.Swagger

Większość usług nad którymi pracuje jest napisana w Nancy, dlatego wciąż niestety edytuję plik ręcznie za pomocą przedstawionego wcześniej edytora online.

Dapper – proste i wydajne narzędzie ORM

Nie opisywałbym kolejnego ORM na blogu, ale myślę, że warto zapoznać się z Dapper, ponieważ jest to dość specyficzna biblioteka. Celem Dapper jest prostota i wydajność. Nie może on zastąpić EntityFramework czy nHibernate, ale w wielu przypadkach, nie musimy korzystać z tak ciężkich i zaawansowanych rozwiązań.

Cały kod źródłowy jest dostępny w jednym pliku:

https://github.com/StackExchange/dapper-dot-net/blob/master/Dapper%20NET40/SqlMapper.cs

Osobiście preferuję jednak NuGet:

image

Załóżmy, że obiekt, który chcemy zmapować wygląda następująco:

class Person { public string FirstName { get; set; } public string LastName { get; set; } }

Dapper to nic innego jak metody rozszerzające dla IDbConnection. Jedną z tych metod jest Query:

SqlConnection sqlConnection = new SqlConnection(@"Server=PIOTR-PC\SQLEXPRESS;Database=Test;Trusted_Connection=True;"); Person[] persons = sqlConnection.Query<Person>("select * from Persons").ToArray(); foreach (Person person in persons) { Console.WriteLine("{0} {1}",person.FirstName,person.LastName); }

Powyższy kod to wszystko, co potrzebujemy, aby zmapować tabelę na obiekt POCO. W Query jako typ generyczny przekazujemy Person, a jako parametry zapytanie SQL. Co więcej (odradzam) możemy skorzystać z dynamic:

SqlConnection sqlConnection = new SqlConnection(@"Server=PIOTR-PC\SQLEXPRESS;Database=Test;Trusted_Connection=True;"); dynamic[] persons = sqlConnection.Query<dynamic>("select * from Persons").ToArray(); foreach (dynamic person in persons) { Console.WriteLine("{0} {1}",person.FirstName,person.LastName); }

W każdym razie w większości przypadków nie będziemy korzystać z dynamic.  Dapper implementuje jeszcze metodę Execute:

public static int Execute(this IDbConnection cnn, string sql, object param) { return Execute(cnn, sql, param, null, null, null); }

Korzystamy z niej, gdy chcemy wykonać SQL, a nie interesuje nas wynik (np. Insert albo Update).

Jak widać, Dapper jest prosty i co za tym idzie, wydajność jest wysoka. Wyniki testów wydajnościowych można przeczytać w oficjalnej dokumentacji. W każdym razie jest niewiele wolniejszy od czystego ADO.NET (SqlConnection). Ze względu na fakt, że Dapper to nic innego jak metody rozszerzające, wszystkie typy baz danych są wspierane.

W Dapper nie ma konfiguracji mapowania. Jeśli kolumny w bazie nie pokrywają się z nazwami właściwości, wtedy możemy:

Person[] persons = sqlConnection.Query<Person>("select FirstName,LastName as Surname from Persons").ToArray();

Moim zdaniem, rozwiązanie doskonałe, gdy zależy nam na wydajności, a nie chcemy korzystać z czystego SqlConnection.

Więcej przykładów można znaleźć tutaj:

https://github.com/StackExchange/dapper-dot-net/blob/master/Tests/Tests.cs

Quartz.NET–planowanie zadań

Czasami potrzeba nam prostego narzędzia, które będzie wykonywało jakieś zadania w określonych ramach czasowych. Można użyć prostego Timera z .NET Framework, ale ma on dość ograniczone możliwości. Na przykład, stan zadań nie może być zapisany w bazie danych. Dla bardzo zaawansowanych rozwiązań, zwykle mamy inną architekturę, na przykład opartą na kolejkach. W takich sytuacjach, zwykle poszczególne technologie posiadają swoje mechanizmy, tak jak nServiceBus o który już wiele razy pisałem.

Dzisiaj jednak chciałbym przedstawić Quartz.NET – lekka biblioteka, która nada się do do prostych przypadków, dla których jednak czysty, standardowy timer ma zbyt małe możliwości.

Standardowo instalujemy pakiet z NuGet:

image

API jest bardzo proste. Standardowy szablon  wygląda następująco:

try { IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler(); scheduler.Start(); Thread.Sleep(1000000); scheduler.Shutdown(); } catch (SchedulerException ex) { Console.WriteLine(ex); }

Koniecznie należy wywołać Shutdown na koniec.  W przeciwnym wypadku, wciąż w aplikacji istniałyby wątki przeznaczone do wykonywania zadań.  Same zadanie definiuje się poprzez implementację interfejsu IJob:

class PrintTextJob : IJob { public void Execute(IJobExecutionContext context) { Console.WriteLine("Test: {0}",DateTime.Now); } }

Następnie należy ją dodać do scheduler’a:

IJobDetail job = JobBuilder.Create<PrintTextJob>().Build(); ITrigger trigger = TriggerBuilder.Create(). StartNow(). WithSimpleSchedule(x => x.WithIntervalInSeconds(10).RepeatForever()).Build(); scheduler.ScheduleJob(job, trigger);

Definicja Triggera jest tutaj chyba najważniejsza. Szczegóły znajdują się oczywiście w dokumentacji, ale istnieje wiele sposób zdefiniowania kiedy zadanie ma wykonać się. Do dyspozycji mamy nawet cron expression:

TriggerBuilder.Create().WithCronSchedule("0 42 10 * * ?")

Można również zdefiniować zachowanie scheduler’a za pomocą pliku konfiguracyjnego:

quartz.scheduler.instanceName = MyScheduler quartz.threadPool.threadCount = 3 quartz.jobStore.type = Quartz.Simpl.RAMJobStore, Quartz

Jak widać, można określić liczbę wątków oraz typ bazy. Domyślnie jest to in-memory, ale nic nie stoi na przeszkodzie, aby zapisać wyzwalacze czy zadania w innym typie bazy.

Quartz wspiera wykonywanie logów i możemy je zdefiniować za pomocą:

Common.Logging.LogManager.Adapter = new Common.Logging.Simple.ConsoleOutLoggerFactoryAdapter {Level = Common.Logging.LogLevel.Info};

Możemy również śledzić wszelkie wykonywane operacje np. poprzez implementacje interfejsu ISchedulerListener:

public interface ISchedulerListener { void JobScheduled(Trigger trigger); void JobUnscheduled(string triggerName, string triggerGroup); void TriggerFinalized(Trigger trigger); void TriggersPaused(string triggerName, string triggerGroup); void TriggersResumed(string triggerName, string triggerGroup); void JobsPaused(string jobName, string jobGroup); void JobsResumed(string jobName, string jobGroup); void SchedulerError(string msg, SchedulerException cause); void SchedulerShutdown(); }

Następnie zaimplementowany listener należy dodać, a po wszystkim oczywiście usunąć:

scheduler.ListenerManager.AddSchedulerListener(mySchedListener); scheduler.ListenerManager.RemoveSchedulerListener(mySchedListener);

Analogicznie sprawa wygląda z ITriggerListener oraz IJobListener:

public interface ITriggerListener { string Name { get; } void TriggerFired(ITrigger trigger, IJobExecutionContext context); bool VetoJobExecution(ITrigger trigger, IJobExecutionContext context); void TriggerMisfired(ITrigger trigger); void TriggerComplete(ITrigger trigger, IJobExecutionContext context, int triggerInstructionCode); } public interface IJobListener { string Name { get; } void JobToBeExecuted(IJobExecutionContext context); void JobExecutionVetoed(IJobExecutionContext context); void JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException); }

Następnie wystarczy wywołać scheduler.ListenerManager.AddJobListener.

Visual Studio 2015: Analiza jakości kodu

VS 2015 usprawnił, moim zdaniem znaczącą sposób analizy kodu. Sam sposób podpowiedzi również został poprawiony i coraz bliżej im do Resharper, a w pewnych sprawach, jak zobaczymy, daje potencjalnie większe możliwości.

Zacznijmy od przykładu pokazanego w prezentacji VS 2015. Załóżmy, że chcemy zaimplementować interfejs ISerializable:

class Sample :ISerializable
{
}

Jeśli tylko namespace zawierający ISerializable nie został dołączony, naciskamy ctrl+. i wyświetli się następujące menu:

image

Nowością jest tutaj podgląd tego, co zostanie zmienione (w tym przypadku dołączenie przestrzeni za pomocą using).

Kolejnym etapem będzie implementacja metod wymaganych przez interfejs. Podgląd będzie wtedy wyglądać następująco:

image

W tej chwili mamy następujący kod:

class Sample : ISerializable
{
   public void GetObjectData(SerializationInfo info, StreamingContext context)
   {
       throw new NotImplementedException();
   }
}

Wspomniałem na początku o nowym mechanizmie analizy kodu. W VS 2015 będzie to rozszerzalne i każdy taki analizator można zainstalować w formie pakietu NuGet. Daje to ogromne możliwości. Zainstalujmy więc jeden z nich:

image

Zaglądając do References->Analysers zobaczymy listę reguł:

image

Wracając do naszego przykładu. Po instalacji powyższego pakietu, zostaniemy poinformowani, że powinniśmy dodać atrybut [Serializable] do stworzonej klasy.

image

Widzimy również numer reguły (CA2237).

Dostępny jest już teraz pakiet reguł dla aplikacji Azure. Opisywany mechanizm, potencjalnie może przydać się do pisania reguł, specyficznych dla danego projektu.

Z drugiej strony, długa jeszcze droga zanim zastąpi to nDepend i Resharper, ale moim zdaniem idzie to w tym kierunku…