czwartek, 22 października 2009

PG.NET XXII Spotkanie grupy


Chciałbym Was zaprosić na XXII spotkanie Poznańskiej Grupy .NET, które odbędzie się w czwartek 29 października 2009 w siedzibie firmy Cognifide (Aleja Wielkopolska 4, Poznań). Będę na nim prezentował bardzo przydatne narzędzie jakim jest NDepend. Oprócz mojej prezentacji nie zabraknie nowinek ze świata .NET, które przedstawi Marek Błotny. Na koniec będzie o Silverlight 3, Blend 3, .NET RIA Services o czym opowie nam Marcin Kruszyński.

Szczegółowa agenda:
18:00 - 18:10 Wstęp
18:10 - 18:30 Nowinki ze Świata .NET (Marek Błotny)
18:30 - 19:00 NDepend (Dariusz Tarczyński)
19:00 - 19:15 Przerwa
19:15 - 21:00 Silverlight 3, Blend 3, .NET RIA Services - najciekawsze cechy (Marcin Kruszyński)

Udział w spotkaniu jest bezpłatny, a rejestracja uczestników znajduje się tutaj: Rejestracja.

EDIT @ 2009.10.30
Prezentację oraz materiały do niej można pobrać stąd

wtorek, 6 października 2009

PG.NET XXI Spotkanie grupy


Gościem XXI spotkania (2009-10-08 19:00, Aleja Wielkopolska 4, siedziba firmy Cognifide) Poznańskiej Grupy .NET będzie Bartosz Nyczkowski, który pracuje w Microsoft, w grupie, która jest odpowiedzialna na wydajność serwerowych wersji Microsoft Windows.

Oto zagadnienia, które omówi:
- Jak wygląda cykl życia projektu w przypadku Microsoft Windows?
- W jaki sposób jest testowana wydajność systemu?
- Jak od strony praktycznej wygląda praca nad usprawnieniami?
- Jak wygląda współpraca z klientami?

Rejestracja na spotkanie

piątek, 18 września 2009

Agile Poznań: III Spotkanie

Spotkanie Poznańskiej Grupy Praktyków Agile odbędzie sie w siedzibie Cognifide
(Al.Wielkopolskie 4, Poznań) 6 października o godzinie 18:00.

Wojciech Buras poprowadzi prezentacje "TDD w prawdziwym swiecie",
podczas której omówione zostaną miedzy innymi następujące kwestie:
1. Najczęstsze problemy z przełożeniem designu na kod i testy.
2. Czym jest i czym nie jest TDD?
3. Przykład ewolucji designu z pomocą TDD.
4. Promowanie testowalności.
5. Zastosowanie na linii frontu.

Warto się wybrać!

poniedziałek, 14 września 2009

PG.NET: XX Spotkanie grupy


Zapraszamy na pierwsze po wakacjach spotkanie Poznańskiej Grupy .NET.
20. spotkanie PG.NET odbędzie się w czwartek 24 września (Aleja Wielkopolska 4, siedziba firmy Cognifide) od godz. 18:00. Zapowiada się bardzo, bardzo interesująco!

Agenda:
18:00 - 18:15 - Wstęp i kilka spraw organizacyjnych
18:20 - 18:35 - Nowinki w świecie .NET - Skrót informacji ze swiata technologii .NET przygotowany przez Marka Błotnego (Cognifide)
18:40 - 19:00 - Hudson Continuous Integration Engine - Kilka informacji na temat alternatywnego narzędzia CI przedstawi Michał Korsak (ITSoftware.pl)
19:15 - 20:00 - Instalator WiX - Bartek Szafko (Advisor)

W czasie spotkania wśród uczestników rozlosujemy kilka licencji na oprogramowanie firm JetBrains, Developer Express, Telerik oraz Nevron.

Po spotkaniu, już tradycyjnie zapraszamy na część dyskusyjno-nieformalną :)
[Rejestracja, strona grupy]

poniedziałek, 7 września 2009

NDepend. Wiesz z kim tańczysz?

Po konkretnej nieobecności w trybie online powracam do życia - z kolejnymi wpisami znanej treści, tym razem słów kilka o niezwykle użytecznym narzędziu: NDepend.

Czym jest owy NDepend? To narzędzie dla .NET, którego autorem jest Patrick Smacchia. Służy ono do badania i raportowania złożoności oraz jakości tworzonego kodu. Na chwilę obecną posiada 82 metryki wyświetlające bardzo szczegółowe dane. Dodatkowo generator raportów sprawiają, że nawet “nieprogramista” może ocenić w jakiej kondycji jest dostarczony mu kod – narzędzie wszechstronne oraz wręcz nieograniczone. Możliwość integracji z NAnt-em lub MSBuild-em dostarczają automatów do wykonywania tego wszystkiego o czym troszkę napisze za chwilę.

Zacznijmy od początku. Po uruchomieniu NDependa wskazujemy zestaw bibliotek, które chcemy poddać analizie. Nie mam nic mądrego pod ręką, dlatego posłużę się bibliotekami MbUnit.

1 

Program od razu przystępuję do akcji, a oczom naszym ukazuje się dłuuugi raport.

2

I tak, na pierwszy rzut widzimy:

ilość instrukcji IL, ilość linii, kodu, (uwaga) pokrycie kodu przez komentarze, ilość klas, ilość typów itd. O ile niektóre z tych danych, z praktycznego punktu widzenia, możemy potraktować statystycznie, o tyle już np. zależności między samymi klasami, martwy kod, konwencje nazewnicze mogą być tym, czego właśnie szukamy.

Kto, kogo, gdzie…

Wspomniane zależności między modułami mogą być bardzo skomplikowane, przedzieranie się pomiędzy nimi za pomocą Reflectora często po kilkudziesięciu minutach powoduje odruch wymiotny na widok kolejnego “dziedzica”. Na szczęście NDepend jest w stanie to wszystko ogarnąć i mimo że kod nadal pozostaje zakręcony, to my w tym gąszczu powiązań dość szybko możemy dojść do celu.

4

Refaktoryzacja kodu z tym okienkiem staje się wiele prostsza. Często graf może wydawać się zbyt obszerny:

DiagramBoxAndArrowGraphBig

Na szczęście do dyspozycji mamy dodatkowe dokowalne okienko: Dependency Matrix:

5

Skóra węża?

Kolejnym bardzo interesującym widokiem jest okno Metrics. Przypomina ono nieco skórę węża (tak, widzę tam skórę węża :-) ), a ma za zadanie wykonanie rentgena naszego kodu: każda bryła w ogólności symbolizuje klasę, która to składa się z mniejszych kwadracików – naszych metod. Im metoda ma więcej linii kodu, tym kwadracik jest większy. Sczegółowość możemy zawężać przełącznikiem widniejącym u góry okienka, natomiast zbiorcze informacje widzimy w okienku Info.

6

CQL

To nie wszystko! NDepend posiada wbudowany interpreter języka zwanego CQL (Code Query Language). Dzięki niemu piszemy kwerendy zbliżone do języka SQL, które to pozwalają odpytywać się NDependa o interesujące nas dane o naszym kodzie.

Operator projekcji: SELECT

  • METHODS – odpytania do metod
  • FIELDS – odpytania do pól
  • TYPES – odpytania do typów
  • NAMESPACES – odpytania do  przestrzeni nazw
  • ASSEMBLIES – odpytania operujące na bibliotekach

Operator informujący: WARN

  • WARN IF – jeśli spełniony jest warunek

Aby utworzyć kwerendę należy skorzystać z okna CQL Query Edition, które posiada wbudowane podpowiadanie składni z możliwością wizualnego modyfikowania jej parametrów:

7

Przykłady:

1. Wyświetl metody publiczne, które mają więcej niż 30 linii kodu:

SELECT METHODS WHERE NbLinesOfCode > 30 AND IsPublic

2. Wyświetl 10 najbardziej złożonych metod i uszereguj metody ze względu na ich złożoność:

SELECT TOP 10 METHODS ORDER BY CyclomaticComplexity

3. Moja konwencja nazewnicza zabrania nazywania pól statycznych zaczynających się od przedrostka m_ Ostrzeż mnie jeśli znajdziesz takie wystąpienia:

WARN IF Count > 0 IN SELECT FIELDS WHERE NameLike "^m_" AND IsStatic

Słowo końcowe

NDepend to naprawdę niezły kombajn, kilka godzin może okazać się niewystarczające, aby zgłębić możliwości jakie drzemią w tym narzędziu. Warto go poznać, gdyż może uratować nam skórę podczas następnej refaktoryzacji kodu.


Promuj

sobota, 16 maja 2009

ASP.NET MVC oraz IIS 6

Hostowanie aplikacji utworzonej za pomocą frameworka ASP.NET MVC na serwerze IIS 6 jest niestety bardzo problematyczne. Utworzone w ten sposób aplikacje wyświetlają niemiły (i w tym przypadku niezrozumiały) komunikat: 404 Not Found. Dlaczego tak się dzieje?

Żądania przychodzące do serwera są przetwarzane poprzez odpowiednie filtry ISAPI (pomijam dla uproszczenia inne zachodzace tam procesy), zgodnie z mapowaniem odpowiednich rozszerzeń. I tak na przykład .ASPX trafia do filtra aspnet_isapi.dll. Routing ASP.NET MVC wymaga użycia modułu IHTTPModule i wywołania UrlRoutingModule. Niestety to wymaga przejścia przez wymieniony wcześniej filtr aspnet_isapi.dll - a ten domyślnie nie jest w tym przypadku żądany, a serwer wyświetla nam błąd 404.

Ok, jak wyjść z opresji?

Metoda 1: Prosta

Odrazu ostrzegam: ta metoda nie nadaje się do zastosowania w dużych, obciążonych witrynach. Zaraz okaże się dlaczego.

Rozwiązanie to opiera się o wildcard. Polega to na tym, że wszystkie żądania przychodzące do serwera będą puszczane przez aspnet_isapi.dll. Zaletą jest prostota, wadą: każde żądanie: nawet o obrazek, plik .css, plik multimedialny i każdy inny plik będzie wiązał się przejściem przez handler DefaultHttpHandler/StaticFileHandler. Takie rozwiązanie oznacza brak cachowania żądań oraz kompresji. Marnotrawstwo zasobów serwera, a to dla mocno obciążonych maszyn oznacza problemy.

Ok, ale dla developerskich zastosowań takie rozwiązanie będzie wystarczające, więc zaczynamy konfigurację:

1. Otwieramy przystawkę Internetowe Uslugi Informacyjne i klikamy właściwości naszej aplikacji MVC.

2. Przechodzimy na kartę: Katalog macierzysty oraz klikamy przycisk Konfiguracja

screen_02 2009.05.16 20.02


3. Klikamy przycisk Insert, wpisujemy rozszerzenie: .mvc potem lokalizację: C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll oraz odznaczamy opcję Sprawdz, czy plik istnieje.

Metoda 2: Zmiana ścieżki routingu

Metoda ta polega na dodaniu rozszerzenia .aspx po nazwie kontrolera, czyli stworzenia ścieżki routingu w ten deseń: {controller}.aspx/{action}/{id}

Niestety tracimy nasze czyste ścieżki, które zapewniało nam MVC.

 

Metoda 3: URL Rewriting

Ta metoda to nic innego jak zautomatyzowanie metody drugiej (odpada nam zmiana ścieżek). Musimy skonfigurować przepisywanie ścieżek tak, aby moduł dopisał znane ASP.NET rozszerzenie i przepuscił je przez odpowiedni filtr.

niedziela, 22 marca 2009

Walidacja danych w ASP.NET MVC za pomocą xVal

Pierwsza zasada programisty: nigdy nie ufaj danym wejściowym. Sama walidacja tych danych może zostać przeprowadzona na wiele sposobów, przekazując odpowiedzialność od warstwy najwyższej do najniższej. Takie podejście pozwala na eliminowanie zagrożenia w wielu przypadkach jeszcze bez przesłania danych do serwera. (Jeśli ktoś nie lubi czytać przydługich wstępów, może przejść od razu do punktu: "Dlaczego xVal?").


Walidacja po stronie warstwy prezentacji
<asp:TextBox ID="TextBox1" runat="server" />
<asp:RequiredFieldValidator ID="reqVal" runat="server" 
       ErrorMessage="Please enter a value" ControlToValidate="TextBox1" />
<asp:Button ID="Button1" runat="server" Text="Button" />



Należy pamiętać, że walidacja po stronie klienta (JavaScript) ZAWSZE musi iść w parze z walidacją po stronie serwera. W przeciwnym razie, to tak jak by w ogóle tej walidacji nie było! Wywołanie metody PageIsValid:

protected void Button1_Click(object sender, EventArgs e) 
{ 
  if (Page.IsValid) 
  { 
    // TextBox1 posiada wartość.
  } 
}



Walidacja po stronie logiki biznesowej

private static void Validate (EmailAddress myEmailAddress) 
{ 
  if (myEmailAddress.ContactPersonId <= 0) 
  { 
    throw new InvalidOperationException("Błąd, brak odpowiedniego ContactPersonId!"); 
  } 
  if (string.IsNullOrEmpty(myEmailAddress.Email))
  { 
    throw new InvalidOperationException("Błąd, brak emaila!"); 
  } 
  if (!IsValidEmailAddress(myEmailAddress.Email)) 
  { 
    throw new InvalidOperationException("Błąd, wprowadzony email jest błędny!"); 
  } 
} 
public static int Save(EmailAddress myEmailAddress) 
{ 
  Validate (myEmailAddress);
  myEmailAddress.Id = EmailAddressDB.Save(myEmailAddress);
  return myEmailAddress.Id; 
} 



Walidacja po stronie serwera SQL

ALTER TABLE  dbo.Category ADD CONSTRAINT
CK_Category CHECK (SortOrder >= 0  AND SortOrder <= 100)
GO   



Wykorzystanie dostępnego frameworka do walidacji danych, np:


Przedstawione frameworki różnią się przeznaczeniem. Ja przedstawię w tym poście świeży jeszcze projekt xVal.


Dlaczego xVal?


  1. Stworzony do użycia z ASP.NET MVC

  2. Wspiera walidację client-side oraz server-side

  3. Możliwość lokalizacji językowej komunikatów

  4. Oparty o przyjęte "dobre techniki"

  5. Możliwość włączenia do projektu bez konieczności przeprowadzania "wielkich" zmian

  6. Lekki, 1 plik .dll do podłączenia

  7. Open source

  8. Mało znany, bo nowy :-)



image-thumb



Zaczynamy. xVal - validation framework for ASP.NET MVC applications.



Po stronie modelu, xVal opiera swoje działanie o atrybuty. Z tego względu atrybuty naszych klas musimy odpowiednio oznaczyć atrybutami. W moim projekcie posiadam klasę Order.cs, którą będę poddawał torturom walidacji:

public class Order
{
    [Required] [StringLength(15)]
    public string ClientName { get; set; }
 
    [Range(1, 20)]
    public int NumberOfProducts { get; set; }
 
    [Required] [DataType(DataType.Date)]
    public DateTime Date { get; set; }
}



Objaśnienie zastosowanych atrybutów:

  • [Required] : dany atrybut jest wymagany, nie może być pusty.

  • [StringLength(n)] : dany atrybut musi posiadać odpowiednią długość, dla typu String.

  • [Range(n, m)] : dany atrybut musi być z przedziału od, do.

  • [DataType(typ)] : dany atrybut musi być zgodny z podanym typem.




Należy jeszcze dodać referencję do: System.ComponentModel.DataAnnotations.dll

Nadszedł czas na kontroler. Ja wszystkie operacje dotyczące składania zamówień będę przeprowadzał w HomeController.

public class HomeController : Controller
{
    [AcceptVerbs(HttpVerbs.Get)]
    public ViewResult CreateOrder()
    {
        return View();
    }
 
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult CreateOrder(Order order)
    {
        OrderManager.PlaceOrder(order);
        return RedirectToAction("Completed");
    }
 
    public ViewResult Completed()
    {
        return View();
    }
}



Klasa OrderManager:

public static class OrderManager
{
    public static void PlaceOrder(Order order)
    {
        var errors = DataAnnotationsValidationRunner.GetErrors(order);
        if (errors.Any())
            throw new RulesException(errors);
 
        // Business rule: Nie można składać zamówień w niedzielę
        if(order.Date.DayOfWeek == DayOfWeek.Sunday)
            throw new RulesException("ArrivalDate", "Nie można skłdać zamówień na niedzielę!", order);
 
        // Todo: zapis do bazy danych, pliku xml lub cokolwiek innego
    }
}



Czas na widok CreateOrder.aspx

<h1>Zamówienie pluszowego misia</h1>
<% using(Html.BeginForm()) { %>
    <div>
        Nazwa: <%= Html.TextBox("order.ClientName") %>
        <%= Html.ValidationMessage("order.ClientName") %>
    </div>
    <div>
        Ilość misiów: <%= Html.TextBox("order.NumberOfProducts")%>
        <%= Html.ValidationMessage("order.NumberOfProducts")%>
    </div>
    <div>
        Data dostawy: <%= Html.TextBox("order.Date")%>
        <%= Html.ValidationMessage("order.Date")%>
    </div>                
 
    <input type="submit" />
<% } %>



Potrzebujemy jeszcze prostą i niezmienną klasę ValidationRunnera, gdyż  DataAnnotations takiej nie posiada:

internal static class DataAnnotationsValidationRunner
{
    public static IEnumerable<ErrorInfo> GetErrors(object instance)
    {
        return from prop in TypeDescriptor.GetProperties(instance).Cast<PropertyDescriptor>()
               from attribute in prop.Attributes.OfType<ValidationAttribute>()
               where !attribute.IsValid(prop.GetValue(instance))
               select new ErrorInfo(prop.Name, attribute.FormatErrorMessage(string.Empty), instance);
    }
}



Musimy jeszcze zmodyfikować napisany wcześniej szkielet metody CreateOrder w kontrolerze HomeController, tak aby błędy były przechowywane w stanie modelu.

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult CreateOrder(Order order)
{
    try {
        OrderManager.PlaceOrder(order);                
    }
    catch(RulesException ex) {
        ex.AddModelStateErrors(ModelState, "order");
    }
 
    return ModelState.IsValid ? RedirectToAction("Completed")
                              : (ActionResult) View();
}



Tym oto sposobem zakończyliśmy walidację po stronie serwera. Czeka nas jeszcze implementacja sprawdzanie po stronie klienta.

screen_02 2009.03.22 14.18




Walidacja po stronie klienta (Client-side validation).
Wszystko czego będziemy potrzebować do wykonania walidacji za pomocą javascriptu, to biblioteka jquery.validate.js oraz xVal.jquery.validate.js (ten znajduje się w folderze z projektem xVal). Należy je skopiować do folderu z naszymi skryptami (/Scripts) oraz podlinkować je do naszego MasterPage.

<head>
    <script type="text/javascript" src="<%= ResolveUrl("~/Scripts/jquery-1.2.6.js")%>"></script>
    <script type="text/javascript" src="<%= ResolveUrl("~/Scripts/jquery.validate.js")%>"></script>
    <script type="text/javascript" src="<%= ResolveUrl("~/Scripts/xVal.jquery.validate.js")%>"></script>
</head>


Czas teraz na import helperów xVal, najlepiej zrobić to raz w pliku Web.config:

<system.web>
  <pages>
     <namespaces>
        <!-- leave rest as-is -->
        <add namespace="xVal.Html"/>
    </namespaces>
  </pages>
</system.web>



Wracamy do widoku oraz dodajemy linijkę służącą do wymuszenia walidacji po stronie klienta. Będzie ona zastosowana dla kontrolek prefixowanych przez nazwę order:

<%= Html.ClientSideValidation<Order>("order") %>



Od tej pory walidacja po stronie klienta powinna być uruchamiana, "potajemnie" korzystając oczwyiście z atrybutów, które definiowaliśmy wcześniej. Super!


W załączniku przesyłam zlokalizowany przeze nnie na nasz język plik z komunikatami.