czwartek, 4 grudnia 2008

Tip: Ostatnio używane projekty Visual Studio

Gdzie Visual Studio przechowuje informacje o ostatnio używanych projektach (MRU List)? Odpowiedź jest prosta: w rejestrze systemu Windows.

mru

Dokładniej jest to: HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\9.0\ProjectMRUList

środa, 3 grudnia 2008

Po XIV spotkaniu PG.NET. Relacja

Kolejne spotkanie Poznańskiej Grupy .NET jest już za nami. Jak zwyklę postaram się przedstawić krótką relację, dla tych, który na spotkaniu być nie mogli.

Pierwsze spotkanie było w moim wykonaniu, dlatego nie będę go oceniał. Fakt, jest taki, że chyba do speaker idola dużo mi brakuje :-) Uświadomił mnie o tym swoim wystąpieniem Mariusz Jarzębowski.

Tematem prezentacji Mariusza była "Historia wstążki". Jak dla mnie kolejne zaskoczenie na plus (bo nie spodziewałem się rocket science). Świetnie przedstawiona ewolucja interfejsów spod stajni Microsoftu. Ogromna pasja przekazana na zewnątrz, ciekawe historie, których próżno szukać w internecie i niezwykle ciekawa osoba Mariusza sprawiły, że nikt na sali nie ziewał :-) Dużo ciekawostek, które wyszly "przy okazji". No i pełen profesjonalizm w przekazywaniu tych wszystkich nowinek. Mała kawka po spotkaniu i wymiana paru zdań z kolegami z grupy, a potem... oczekiwanie na kolejne spotkanie!

PG.NET XIV Spotkanie już dziś!

Zapraszam wsyzstkich na 14 sporkanie Poznańskiej Grupy .NET. Gościem specjalnym będzie Mariusz Jarzębowski, ktory opowie o histrii wstążki (The Storry of the Ribbon).

No a drugim gościem będe ja :-) Opowiem, jak podejść do tworzenia nowoczesnych aplikacji ASP .NET, czyli niezbędny toolbox: JavaScript, ASP .NET Ajax, itp.

Start: 17:45 w siedzibie firmy Cognifide

Rejestracja i agenda

sobota, 29 listopada 2008

Tip: Jak pobrać tytuł strony www. Niezawodnie.

Jest kilka sposobów na odczytywanie dokumentów tekstowych i wyszukiwanie w nich określonych treści. Często najbardziej niezawodne jest użycie do tego celu wyrażeń regularnych, czyli inaczej mówiąc regexa. Oto przykład jak pobrać tytuł witryny:

public string GetHTMLPageTitle(string file)
{
    Match m = Regex.Match(file, @"<title>\s*(.+?)\s*</title>");
    if (m.Success)
        return m.Groups[1].Value;
    else
        return string.Empty;    
}

Może się przydać :-)

piątek, 28 listopada 2008

Relacja po XIII spotkaniu PG.NET

Kto nie był, niech żałuje. Po pierwsze gratulacje dla zespołu Cognifide za pokaz ich (chyba sztandarowego) dzieła: EpiServera CMS. O ile byłem troszkę sceptycznie nastawiony do prezentacji, gdyż nie liczyłem na nic odkrywczego, to po prezentacji byłem zaskoczony na plus! Bardzo dobry przykład wykorzystania technologii .NET w stworzeniu naprawdę dużego narzędzia bijącego moim zdaniem Sharepointa na głowę. Kilka mechanizmów EpiServer mnie nawet zainspirowało - przedewszystkim ten rozszerzalny framework.

Inauguracja PLSSUG i prezentacja Stefana rozwiała przede wszystkim mój niedawny problem. Druga sprawa, to fajnie jest być na bieżąco jeśli chodzi o SQL Servera. Prezentacja również jak najbardziej udana. Stefan to niezły geek sqlowy :-). Taaak rozumiem jak to jest kiedy się przychodzi do domu, a tu nikt nie wie o czym się mówi :-)

Podsumowując: kolejny udany wieczór z kodem :-)

poniedziałek, 24 listopada 2008

PG.NET: XIII spotkanie już w czwartek


Na czwartek proszę, aby każdy poznański (ale mile widziana cała Polska!) developer .Net zaplanował sobie wieczór tak, aby móc wybrać się na spotkanie PG.NET
Na szczęśliwej, trzynastej odsłonie zaprezentowany zostanie projekt: EpiServer CMS firmy Cognifide (Adam Najmanowicz) oraz (uwaga) wystartuje PLSSUG (Polish SQL Server User Group) Poznań (Stef Konopnicki)!!
Agenda i rejestracja dostępna jak zwykle na stronie grupy: http://ms-groups.pl/pg.net/default.aspx
Obecność obowiązkowa!

IT Academic Days na Politechnice Poznańskiej

Mam przyjemność zaprosić wszystkich programistów i nie-programistów na imprezę Microsoft IT Academic Days, która odbędzie się 4 grudnia w Centrum Wykładowym Politechniki Poznańskiej. ITAD to cykl akademickich wykładów na których można zobaczyć i usłyszeć wiele ciekawych informacji o technologiach i innowacyjnych rozwiązaniach firmy Microsoft, ale nie tylko. Będę miał na niej swoje "pięć" minut przedstawiając wykład pt. "Trzej muszkieterowie, czli ASP .NET MVC". Start imprezy o godzinie 9:00. Dostępna jest już pełna Agenda. Zachęcam wszystkich gorąco, mimo że zimno :-) !

Wstęp jest bezpłatny, po wcześniejszej rejestracji na portalu CodeGuru. Strona konferencji: http://itacademicday2008.studentlive.pl/

poniedziałek, 17 listopada 2008

Data jest data?!

Operacje na datach to pewnie chleb powszedni dla każdego programisty. Wstawianie ich do bazy danych, to kolejna, seryjna nasza czynność. W zasadzie bardziej chodzi mi o umieszczanie domyślnych wartości daty w tabelach bazy danych MS SQL Server. W sumie niebyłoby nic odkrywczego, gdyby nie fakt, że wyjątki w takim kodzie pojawiają się w najmniej oczekiwanych momentach :-)

Istnieje spora różnica między wartościami: DateTime.MinValue, który w rezultacie da: 01-01-0001, a SqlDateTime.MinValue, który zwróci: 01-01-1753. Cała zabawa powoduję, że podczas próby wstawienia rekordu, którego wartość w kolumnie typu DateTime jest generowana w kodzie programu przez DateTime.MinValue otrzymujemy wyjątek przekroczenia wartości. Dlatego, aby wstawić minimalną wartość typu DateTime do bazy danych należy zawsze posługiwać się SqlDateTime.MinValue.

Jeszcze ciekawsza systuacja jest przy porównaniu wartości: DateTime.MaxValue oraz SqlDateTime.MaxValue. Zróbmy proste zadanie: wstawmy do tabeli w naszej bazie danych wartość DateTime.MaxValue, potem odczytajmy tą wartość do zmiennej lokalnej tego samego typu. Na szybko naskrobałem coś takiego:

static void Main(string[] args)
{
    DateTime fromDb = DateTime.MaxValue;
    DateTime toDb = DateTime.MaxValue;
    SqlConnection conn = new SqlConnection(@"Data Source=TOSHIBA\SQLEXPRESS;Initial Catalog=Poligon;Integrated Security=True");
    try
    {
        conn.Open();
        SqlCommand cmd = new SqlCommand("INSERT INTO Daty VALUES(@data)", conn);
        cmd.Parameters.Add("data", System.Data.SqlDbType.DateTime).Value = DateTime.MaxValue;
        cmd.ExecuteNonQuery();
        SqlCommand getCmd = new SqlCommand("SELECT TOP(1) DAT_Data FROM Daty", conn);
        using (IDataReader rdr = getCmd.ExecuteReader())
        {
            while(rdr.Read())
            {
                fromDb = Convert.ToDateTime(rdr["DAT_Data"]);
            }
        }
        bool areSame = (DateTime.Compare(toDb, fromDb) == 0);
        TimeSpan subDates = toDb.Subtract(fromDb);
        int result = subDates.Milliseconds; //nasz wynik
    }
    finally
    {
        conn.Close();
        conn.Dispose();
    }
}

Że co?


Metoda DateTime.Compare nie zwróciła nam wartości 0 (zero), co oznaczałoby, że data zapisana do bazy i ta odczytana są takie same. Zwróciła nam wartość, która oświadcza nam, że wartość otrzymana z bazy danych jest mniejsza od tej, którą zapisaliśmy.


Zaglądamy do zmiennej result...


Zmienna result trzyma dla nas różnicę obu dat. Po odjęciu wartości otrzymanej z bazy danych od tej którą zapisywaliśmy dostajemy różnicę wynoszącą... dwie równiutkie milisekundy.


Prawie robi wielką różnicę


DateTime.MaxValue to: 31-12-9999 23:59:59.999, natomiast maksymalna wartość daty dla bazy danych, czyli to co zwraca SqlDateTime.MaxValue to: 31-12-9999 23:59:59.997 Różnica dwóch milisekund występuje w momencie konwersji DateTime do SqlDateTime, tu nie otrzymujemy wyjątku - a powinniśmy gdyż zakresy nieznacznie, bo nieznacznie, ale różnią się. Baza danych posłusznie przyjmuje wartość potajemnie ucinając 2 ms. Biednemu developerowi wydaje się że wszystko jest ok (bo narzędzia podglądu danych oraz debugger nie wyświetlają wartości z tak dużą precyzją). Pytanie za 100 punktów: co powodują taką różnice? Dlaczego nie można było tej wartości wydłużyć o te 2 nieszczęsne milisekundy?

Inspektor Gadget, czyli śledzenie aplikacji cz. III

NLog - projekt o polskich korzeniach!

Tak, tak - nie tylko prezydent USA ma polskie korzenie! Ma je także NLog, gdyż został napisany przez polskiego programistę: Jarka Kowalewskiego. Ale nie dlatego chcę przedstawić tą bibliotekę jako wartą wypróbowania. Jest to biblioteka, która z pewnością dostarczy nam wszystkich niezbędnych mechanizmów potrzebnych do logowania zdarzeń w naszych aplikacjach. Niestety aktualnie autor nie uczestniczy tak aktywnie w projekcie jak kiedyś.

Zanim przystąpimy do implementacji gorąco zachęcam do zapoznania się z poprzednim artykułem poświęconym log4net, a także wstępniakiem do całej serii Wystarczy, że jako czytelnik poznasz zasadę rejestracji zdarzeń w log4net, gdyż w NLog jest ona analogiczna.

Zaczynamy przygodę z NLog

Pierwsze co musimy zrobić, to ściągnąć binarki z oficjalnej strony projektu. Ja do celów tego artykułu wykorzystam wersję nlog-1.0-setup.exe. Dzięki tej paczce oprócz samych plików dll otrzymamy także dokumentację i zbiór przykładów. Po instalacji dodajemy referencję do NLog.dll znajdującym się w folderze net-2.0. Następnie do projektu dodajemy plik konfiguracyjny NLog.config - plik ten zostanie automatycznie wczytany i NLog z niego pobierze sobie konfigurację. Ustawmy mu właściwość "Copy to Output Directory" na: "Copy always". Wyjście naszych logów - o priorytetach: Debug wzwyż - będziemy kierować na konsolę, dlatego do pliku konfiguracyjnego powinniśmy dodać wpis: "targets".

<?xml version="1.0" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <targets>
    <target name="console" xsi:type="Console"
            layout="${longdate}|${level}|${message}"/>
  </targets>
</nlog>

Cel mamy zdefiniowany, teraz pora na definicję zasad: tak jak ustaliliśmy wcześniej interesuje nas jedynie logowanie zdarzeń o priorytecie Debug i wyższych. Dodajmy więc definicję rules poniżej definicji targets:


<rules>
    <logger name="*" minlevel="Debug" writeTo="console" />
</rules>

Sama zasada wstawiania zdarzeń do logów jest praktycznie identyczna jak w log4net. Pierwsze co musimy zrobić to otrzymać instancję klasy Logger. Robimy to wywołując statyczną metodę klasy LoggerManager:


using System;
using NLog;
namespace NLogSample
{
    class Program
    {
        private static Logger logger = LogManager.GetCurrentClassLogger();
        static void Main(string[] args)
        {
            logger.Debug("Nasz pierwszy komunikat NLog!");
        }
    }
}

Po testowym uruchomieniu efekt powinien być podobny do poniższego:


nlog1


Ok, ale logowanie na konsolę to na pewno nie jest to, co docelowo chcielibyśmy osiągnąć. Spróbujmy zatem tym razem logować zdarzenia do pliku tekstowego: chyba jednej z czytelniejszej formy logowania zdarzeń (choć problem z filtrowaniem ich to inna sprawa). Aby nie niszczyć pracy jaką włożyliśmy w poprzedni przykład :-) nasza aplikacja do pliku tekstowego będzie logowała zdarzenia o priorytetach Error i wyższych, reszta niech idzie na konsolę, tak jak było to dotychczas. Dodajmy więc nową regułę do pliku konfiguracyjnego NLog.config:


<logger name="*" minlevel="Error" writeTo="file" />

Czas na dodanie wpisu konfiguracyjnego samego celu:


<target name="file" xsi:type="File"
            layout="${longdate} ${logger} ${message}"
            fileName="${basedir}/logfile.txt"
            keepFileOpen="false"
            encoding="iso-8859-2" />

Myślę, że poszczególne atrybuty są na tyle czytelne, że nie trzeba ich szerzej opisywać, ciekawskich odsyłam do dokumentacji :-) Po modyfikacjach plik NLog.config powinien wyglądać następująco:


<?xml version="1.0" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <targets>
    <target name="console" xsi:type="Console"
            layout="${longdate}|${level}|${message}"/>
    <target name="file" xsi:type="File"
            layout="${longdate} ${logger} ${message}"
            fileName="${basedir}/logfile.txt"
            keepFileOpen="false"
            encoding="iso-8859-2" />
  </targets>
  <rules>
        <logger name="*" minlevel="Debug" writeTo="console" />
        <logger name="*" minlevel="Error" writeTo="file" />
  </rules>
</nlog>

Czas na odrobinę kodu, przypominam dodajemy logowanie zdarzeń typu Error do pliku tekstowego:


using System;
using NLog;
namespace NLogSample
{
    class Program
    {
        private static Logger logger = LogManager.GetCurrentClassLogger();
        static void Main(string[] args)
        {
            logger.Debug("Nasz pierwszy komunikat NLog!");
            int a = 1;
            int b = 0;
            try
            {
                int c = a / b;
            }
            catch (Exception ex)
            {
                logger.Error(ex);
            }
        }
    }
}

Wystarczy nam teraz uruchomić aplikację i przekonać się, że w folderze ze skompilowanym programem został utworzony plik logu, w którym będą lądowały wyjątki przechwycone w blokach try-catch.
Oczywiście to dopiero ułamek potęgi biblioteki NLog. Oprócz jej niewątpliwej szybkości (sprawdzonej w kilku benchmarkach), należy odnotować mnogość celów dla zdarzeń, czyli:

• Class Description
• ASPNetTraceTarget 
• ChainsawTarget 
• ConsoleTarget 
• DatabaseParameterInfo
• DatabaseParameterInfoCollection
• DatabaseTarget
• DebuggerTarget
• DebugTarget
• FileTarget
• FormControlTarget 
• MailTarget
• MemoryTarget
• MessageBoxTarget
• MethodCallParameter
• MethodCallParameterCollection
• MethodCallTarget
• NetworkTarget
• NLogViewerTarget 
• NullTarget 
• RichTextBoxTarget
• TraceTarget 
• WebServiceTarget

Jako zadanie domowe mogę zachęcić Was do zalogowania zdarzeń do kilku innych wyjść.


środa, 12 listopada 2008

Tip: Możesz wywołać breakpoint nie ustwiając go w IDE

Witam :-)
Pewnie 99% przypadków zatrzymywania kodu w tzw. breakpoincie dokonywana jest przez oznaczenie odpowiedniej linijki kodu jako właśnie breakpointa, klikając na marginesie edytora kodu Visual Studio w linii w której ma nastąpić przerwanie. Istnieje jednak inna możliwość jego wywołania: programowa. Wystarczy wywołać: Debugger.Break(). Wcześniej należy zaimportować przestrzeń nazw System.Diagnostics.

Pilnie kupię Windows Vista ;-)

Przygoda z Windows Azure była niczym sen, który pewnego wieczora (jakieś 5 min temu) prysnął. Jestem jeszcze przedpotopowym developerem pracującym na... XP-ku. Chyba czas iść do sklepu... ehhh.

niedziela, 9 listopada 2008

Democamp Poznań. Targi startupów.

20 listopada 2008 w Pawilonie nr. 11 Międzynarodowych Targów Poznańskich odbędzie się impreza "Democamp", czyli targi przeznaczone dla startupów. Na targach, oprócz projektów, zagoszczą także spółki Venture Capital oraz będziemy mogli poznać działalność akademickich inkubatorów przedsiębiorczości. Impreza obowiązkowa dla wszystkich, który chcą zaistnieć w sieci wraz ze swoim projektem!

Źródło: http://democamp.pl/

sobota, 8 listopada 2008

Inspektor Gadget, czyli śledzenie aplikacji cz. II

log4net wchodzi na scenę

Podstawowymi klasami biblioteki log4net z jakimi będzie pracował programista są: LogManager oraz Log. LogManager pełni funkcję kontentera i zarządcy nad Loggerami. Cały system może składać się z hierarchii Loggerów – klas dostarczających metod do logowania. Aby otrzymać konkretną nazwaną instancję klasy Logger posługujemy się statycznymi metodami GetLogger obiektu LoggerManager.



Żądania logowania wykonywane są przez wykonanie określonej metody w zależności od typu zaistniałego zdarzenia, czyli: DEBUG, INFO, WARN, ERROR, FATAL
Zdarzenie zostanie zalogowane przez dany Logger jeśli ma taki sam lub wyższy priorytet. Z kolei każdy Logger może posiadać filtr w którym określono na zdarzenia jakiego typu (priorytetu) ma reagować. Jeśli do danego Loggera nadejdzie żądanie zalogowania zdarzenia niższego niż jest przez niego obsługiwane, to nie zostanie ono zalogowane. Warto zauważyć także, że każdy typ logów może mieć określony sposób formatowania, jest to szczególnie istotne zwłaszcza jeśli część z nich wysyłamy np. mailem w formacie HTML, a część logujemy w bazie w postaci czystego tekstu.

Konfiguracja log4net
Żaden system bez odpowiedniej konfiguracji nie może działać poprawnie. Na szczęście log4net dostarcza szeroki wachlarz możliwości konfiguracji zarówno przez pliki XML jak i programowo. Po pierwsze musimy pobrać log4net z witryny http://logging.apache.org/log4net/download.html i dodać referencję do jego głównej biblioteki log4net.dll. W niniejszym przykładzie będę posługiwał się wersją 1.2.10. Sam log4net nie wymaga wielkiego nakładu na jego konfigurację aby zaczął działać w swojej podstawowej formie. W zasadzie można powiedzieć, że domyślne ustawienia środowiska są wystarczające, aby zacząć przygodę z log4net i sprawdzić, czy w ogóle jest to warte zachodu :-)

using System;
using System.Collections.Generic;

using log4net;
using log4net.Config;

namespace ConsoleApplication1
{
class Program
{
private static readonly ILog MyLogger = LogManager.GetLogger(typeof(Program));

static void Main(string[] args)
{
BasicConfigurator.Configure(); //konfiguracja podstawowa, bez pliku xml
MyLogger.Info("Aplikacja została uruchomiona");

}
}
}



Aby zmusić log4net do pobrania swojej konfiguracji z pliku konfiguracyjnego aplikacji, programista musi jedynie stworzyć odpowiednie wpisy konfiguracyjne oraz wymusić załadowania nastawów z pliku. Spróbujmy dodać plik App.config i wypełnić go podobnie jak na schemacie poniżej.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender" >
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%ndc] - %message%newline" />
</layout>
</appender>
<root>
<level value="INFO" />
<appender-ref ref="ConsoleAppender" />
</root>
</log4net>
</configuration>



Teraz wystarczy jedynie załadować konfigurację wywołując metodę XmlConfigurator.Configure();

Logujemy!

Stwórzmy prostą aplikację, która swoją konfigurację do logowania pobierze z pliku konfiguracyjnego aplikacji, a logi typu INFO oraz DEBUG umieszcza na konsoli. Pierwsze co musimy zrobić do dodać odpowiedni plik App.config. Ze względu na to, że w naszym systemie chcemy logować dwa typy zdarzeń: informacyjne (INFO) oraz debugowe (DEBUG) musimy stworzyć wpis appender, czyli naszego wyjścia. Wewnątrz wpisu appendera należy podać sposób formatowania pojedynczego wpisu logu.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender" >
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%ndc] - %message%newline" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="ConsoleAppender" />
</root>
</log4net>
</configuration>



Dostępne w log4net wyjścia dla naszych logów to:
• AdoNetAppender,
• MS SQL Server,
• MS Access,
• Oracle9i,
• Oracle8i,
• IBM DB2,
• SQLite,
• AspNetTraceAppender,
• BufferingForwardingAppender,
• ColoredConsoleAppender,
• ConsoleAppender,
• EventLogAppender,
• FileAppender,
• ForwardingAppender,
• MemoryAppender,
• NetSendAppender,
• OutputDebugStringAppender,
• RemotingAppender,
• RollingFileAppender,
• SmtpAppender,
• SmtpPickupDirAppender,
• TraceAppender,
• UdpAppender

Uważny czytelnik zauważył pewnie, że nie zdefiniowaliśmy celu naszych logów dla zdarzeń INFO. Tak jak wspominałem wcześniej zdarzenia mają określoną hierarchię i w przypadku wystąpienia zdarzenia o priorytecie >= zdefiniowanemu zostanie on na pewno obsłużony, a o niższym priorytecie - nie.
Wpis o priorytecie INFO jest nad wpisem DEBUG więc wyląduje na naszej konsoli. Podobnie stanie się z wpisami o priorytetach ERROR i FATAL - te akurat zdarzenia powinny być umieszczane w blokach try-catch i automatycznie gromadzić wyjątki np. w bazie danych. Po dokładną specyfikację priorytetów odsyłam do oficjalnej strony projektu log4net .
W systemie może istnieć tyko jeden logger główny: root, reszta logerów dziedziczą po nim tworząc swoiste drzewo dziedziczenia.

namespace ConsoleApplication1
{
class Program
{
private static readonly ILog MyLogger = LogManager.GetLogger(typeof(Program));

static void Main(string[] args)
{
XmlConfigurator.Configure();
MyLogger.Info("Aplikacja została uruchomiona");
If (args != null)
MyLogger.Debug(string.Format("Argumenty uruchomieniowe: {0}", args));
MyLogger.Info("Aplikacja została zakończona");
}
}
}



Efekt powinien być podobny do tego:



W ten oto sposób poprawnie skonfigurowaliśmy oraz zalogowaliśmy pierwsze zdarzenia za pomocą biblioteki log4net! Chyba nie było to trudne?

Do pobrania:

Tip: Jak przyśpieszyć uruchamianie projektu?

Istnieje pewna sztuczka w Visual Studio, która pozwala za skrócenie czasu uruchamiania się i budowania projektów. Kiedy plik solution składa się z wielu projektów, wystarczy zaznaczyć, aby budowany był jedynie projekt startowy oraz jego zależności.
Tools – Options – Projects and Solutions – Build and Run i zaznaczamy Only build startup projects and dependencies on Run

czwartek, 6 listopada 2008

Inspektor Gadget, czyli śledzenie aplikacji cz. I

Problem śledzenia działania aplikacji oraz eliminowania błędów jest tak stary jak stare jest programowanie. Pierwsze języki programowania nie posiadały rozbudowanych środowisk programistycznych oraz tak zaawansowanych technik śledzenia wykonywania kodu programu. Wraz z rozwojem samych języków, rozwoju ulegały także zintegrowane środowiska wytwarzania oprogramowania. Dziś chyba trudno sobie wyobrazić programowanie, a tym bardziej wyłapywanie błędów bez debugera.
Chciałbym przyjrzeć się w jaki sposób możemy najefektywniej śledzić działanie aplikacji oraz wychwytywać w nich błędy. Wszystko po to, aby nasze programy mogły działać jeszcze szybciej i bezbłędnie.

Podejście pierwsze – najprostsze
Chyba każdy z nas stosował lub nawet stosuje do dziś popularne wyświetlanie zawartości interesujących nas zmiennych na ekranie (konsoli). Popularna technika jest chyba najstarszą formą debugowania programów. Jej podstawową zaletą jest to, że bardzo łatwo ją zaimplementować oraz daje przejrzyste wyniki. Na tym chyba zalety się kończą. Wad jest sporo: kod służący do śledzenia kompilowany jest to programu wynikowego, trudno w nim śledzić bardziej zaawansowane powiązania z innymi zmiennymi itd.

Na szczęście takie podejście do problemu śledzenia aplikacji należy raczej do rzadkości, a metodologie dostępne dla każdego programisty dają znacznie lepsze rezultaty. Innym pewnym rozwinięciem przedstawionej wyżej techniki jest zapis stanu zmiennych do pliku tekstowego. To również popularna technika ze względu na łatwość zapisu do pliku w większości języków programowania. W dalszej części artykułu postaram się je przedstawić w jaki sposób za pomocą dostępnych narzędzi i bibliotek możemy podejść do problemu w bardziej zaawansowany sposób.

Biblioteki logowania
Czym są biblioteki programowania? Są to specjalizowane biblioteki stworzone po to aby programista nie musiał przejmować się samym sposobem wyświetlania, czy zapisu komunikatów diagnostycznych. Praca z nimi zazwyczaj sprowadza się do wywołania odpowiedniej metody, a biblioteka sama dba o odpowiedni przepływ komunikatów diagnostycznych. Należy w tym momencie nadmienić, iż powstał pewien podział na komunikaty diagnostyczne, które zostały oddzielone od komunikatów błędów. Często prowadzi to do innego (definiowanego przez programistę) zachowania się bibliotek logowania w przypadku wystąpienia błędu, a innego w momencie rejestracji informacji czysto diagnostycznych.
W Internecie istnieje wiele bardzo dobrych rozwiązań służących do tego celu. Najciekawsze z nich to:
1. log4net
2. NLog
3. Microsoft Enterprise Library Logging Application Block

Pierwsza z przedstawionych bibliotek jest portem doskonałej javowej biblioteki log4j. Posiada ona bogatą grupę projektów, które z jej rozwiązań korzystają oraz - co jest zaletą – jest dość dojrzałym i stabilnym projektem. Nie ma najmniejszych problemów z dostosowaniem przepływu raportowania, a lista celów naszych logów jest długa.
NLog został stworzony przez polskiego programistę: Jarosława Kowalewskiego . Zawiera wiele celi logowania oraz pozwala na bardzo szeroką konfigurację. Niestety projekt nie jest już tak dynamicznie rozwijany – w zasadzie można powiedzieć o jego wstrzymaniu, choć na oficjalnej stronie takich informacji nie znajdziemy. Nie zmienia to faktu, iż w obecnej formie projekt pozwala na wykonanie wszystkich popularnych operacji zarówno raportowania błędów jaki i informowania o stanie aplikacji.
W kolejnych częsciach artykułów będę starał się przedstawić jak z tych dobrodziejstw korzystać. Zapraszam do odwiedzania!
Nich kod będzie za Wami!

niedziela, 2 listopada 2008

CodeGuru: Wykład pt. "ASP .NET Ajax"



Chciałbym wszystkich zaprosić na mój wykład, który odbędzie się 4 listopada 2008 r. na Politechnice Poznańskiej, a na którym będę starał się przedstawić wstęp do "ASP .NET Ajax". Spotkanie to odbywa się w ramach studenckich grup .Net.
Rejestracja na spotkanie jest możliwa przez portal CodeGuru.pl pod adresem: http://codeguru.pl/Default.aspx?Page=Events/ShowEventDetails&eventId=1814

Dodano: 04.11.2008
Przykłady oraz prezentacja (mirror na CodeGuru.pl):

piątek, 31 października 2008

PG.NET: Po XII spotkaniu

Wczoraj odbyło się dwunaste spotkanie "Poznańskiej Grupy .Net". Tematem spotkania było:
Composite Application Guidance for WPF: temat zaprezentował Szymon Kobalczyk. Drugi temat to: IronPython zaprezentowany przez Aleksandra Sumowskiego.
Refleksje. CAG dla WPF, wydał się dla mnie świetnym uzupełnieniem Inversion of Control omawianago dzień wcześniej podczas spotkania grupy .Net na Politechnice Poznańskiej. Było widać, że Szymon godzinami może rozmawiać o WPFie... i tak też się stało :-) Dwugodzinna prezentacja okazała się niezłą przeprawą szczególnie dla ludzi niesiedzących w core WPF-a, czyli np. mnie :-) Do następnego wykładu przetrwała garstka :-)
Inaczej poprowadził spotkanie Aleksander, który w dość prosty, a zarazem jak najmniej "sztywny" (nie to,żeby Szymon prowadził sztywno!) sposób omawiał potęgę IronPythona i CPythona przy okazji. Sporo się uśmiałem, ale prezentacja była bardzo, bardzo interesująca.
Oby tak dalej!!

czwartek, 30 października 2008

MS Groups: It's not a bug, it's feature?

Ostanio postanowiłem sie zalogować na MS Groups, aby dodać zaktualizowane informacje w moim profilu. Ok, hasło mi umknęło z pamięci więc skorzystałem z opcji "Przypomnij hasło", dostałem nowe na maila, loguję się i wchodzę do profilu. Tylko chwila to chyba nie mój profil?!!

Ok, to teraz przejrzyjmy innych biednych userów grupy. Szukamy naszego Iroen... jest!

Patrzymy kto kryje się pod tym zagadkowym nickiem...

I w ten sposób wystarczy przejrzeć inne osoby, aby dowiedzieć się, że profile linkują do kompletnie innych wpisów, coś mi się wydaję, że komuś się pomieszały ID-iki w bazie :-)
Perhaps - It's not a bug, it's feature.

środa, 29 października 2008

Darmowa wersja CodeRush wydana!

Światło dzienne ujrzała darmowa wersja świetnego dodatku do Visual Studio ułatwiająca pracę programiście poprzez kombinacje klawiaturowe oraz nowe metody refaktoringu kodu. Sam używam tego dodatku i wg. mnie jest on bardzo dobry, niestety za pełną wersję produktu trzeba słono zapłacić.
Strona produktu: DevExpress CodeRushX

wtorek, 28 października 2008

Gotowiec: Mega prosta ankieta w 2 minuty

Niedawno zostałem poproszony przez mojego kolegę, czy nie mógłbym mu stworzyć prostej ankiety? I tak oto w ciągu 1 minuty, 40 sekund powstała mega prosta ankietka, opierająca swoją konfigurację o plik Web.config - to tutaj definiowane są pytania i odpowiedzi na nie. Z tego względu, że często można przeczytać prośby początkujących o podobne rozwiązania postanowiłem tu niniejszy projekt umieścić. Zastrzegam, rozwiązania ma swoje wady, jednak w niecałe dwie minuty nie da się wszystkiego dorobić :-)

Strona Default.aspx
<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Panel ID="pnlSurvey" runat="server">
<asp:Label ID="lblQuestion" runat="server" />
<asp:Repeater ID="rptAnswers" runat="server"
onitemdatabound="rptAnswers_ItemDataBound">
<HeaderTemplate>
<h3>Możliwe odpowiedzi:</h3>
</HeaderTemplate>
<ItemTemplate>
<asp:CheckBox ID="chckAnswer" runat="server" /><br />
</ItemTemplate>
</asp:Repeater>
</asp:Panel>
<asp:Button ID="btnNext" runat="server" Text="Dalej >>" onclick="btnNext_Click"
Visible="False" />
<br /> <asp:HyperLink ID="HyperLink1" runat="server"
NavigateUrl="Default.aspx?QuestionNo=0">Początek ankiety</asp:HyperLink>
</div>
</form>
</body>
</html>


Kod strony Default.aspx.cs
using System;
using System.Configuration;
using System.Web.UI.WebControls;

public partial class _Default : System.Web.UI.Page
{
public string[] Questions
{
get
{
if (ConfigurationManager.AppSettings["Questions"] != null)
{
return ConfigurationManager.AppSettings["Questions"].ToString().Split(';');
}
return new string[] { };
}
}

public int ActualQuestionNo
{
get
{
if (Request.QueryString["QuestionNo"] != null)
return Convert.ToInt32(Request.QueryString["QuestionNo"]);
return -1;
}
}

protected void Page_Load(object sender, EventArgs e)
{
if (ActualQuestionNo >= 0)
{
PrepareAnswers(PrepareQuestion());
btnNext.Visible = true;
}
}

private void PrepareAnswers(int p)
{
if (ConfigurationManager.AppSettings["Answers"] != null)
{
string[] answers = ConfigurationManager.AppSettings["Answers"].ToString().Split(';');
if (p < answers.Length)
{
string[] anwersQ = answers[p].Split(',');
if (anwersQ.Length >= 1)
{
rptAnswers.DataSource = anwersQ;
rptAnswers.DataBind();
}
}
}
}

private int PrepareQuestion()
{
int questionNo = ActualQuestionNo;
if (questionNo >=0)
{
if (questionNo <= Questions.Length -1 )
lblQuestion.Text = Questions[questionNo];
}

return questionNo;
}


protected void rptAnswers_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
CheckBox chck = e.Item.FindControl("chckAnswer") as CheckBox;
if (chck != null)
chck.Text = e.Item.DataItem as string;
}
}
protected void btnNext_Click(object sender, EventArgs e)
{
if (ActualQuestionNo >= 0)
{
if (ActualQuestionNo < Questions.Length - 1)
{
//Zachowaj Odpowiedz
int toQuestion = ActualQuestionNo + 1;
Response.Redirect(string.Format("Default.aspx?QuestionNo={0}", toQuestion));
}
else
Response.Redirect("SurveyEnd.aspx");
}
}


}


Do Web.config-a należy dodać następujące wpisy:
    <appSettings>
<add key="Questions" value="Jakie masz ulubione danie?; Jakie lubisz marki samochodów?; Twoje zainteresowania"/>
<add key="Answers" value="Pizza, Gołąbki, Schabowy; Alfa Romeo, Audi, Fiat, Porshe; IT, Film, Muzyka"/>
</appSettings>


Tada!! Ankieta gotowa!!
Niech kod będzie za Wami!

poniedziałek, 27 października 2008

.NET ma nowe logo!

Faktem jest, że stare logo nie powalało na kolana, ale w sumie to nie miało być dzieło sztuki. Nowe nawiązuje min. do loga technologii Silverlight. Mnie osobiście podoba się średnio. Wolałbym, aby graficy odświeżyli stary layout loga, bo to jest takie nijakie... A Wam jak się podoba?

Mocking. Tworzymy i testujemy aplikację. odcinek II.

Zajmijmy się wykorzystaniem mockingu w praktyce. Do tego celu posłuży nam zaczątek kodu, który napisałem w poprzednim odcinku, czyli aplikacja TaxiService.
Główna logika biznesowa będzie zawarta w klasie TaxiService.cs, która to implementuje interfejs ITaxiService:


using System;

namespace MockingTaxiService
{
interface ITaxiService
{
bool Order(DateTime doDate, string toAddress, int clientID);
}
}

Implementacja interfejsu:


using System;

namespace MockingTaxiService
{
public class TaxiService : ITaxiService
{
#region ITaxiService Members

public bool Order(DateTime doDate, string toAddress, int clientID)
{
System.Threading.Thread.Sleep(5000);
return true;
}

#endregion
}
}
Prosta klasa klienta:


using System;

namespace MockingTaxiService
{
public class TaxiClient
{
public bool OrderTaxiTooday(string toAddress, int clientID)
{
TaxiService svc = new TaxiService();
return svc.Order(DateTime.Now, toAddress, clientID);
}

}
}

Jak można zauważyć samo wykonanie metody Order trwa niekrócej niż 5 sekund, ale prosto sobie wyobrazić przypadki, kiedy taka metoda może zabrać znacznie więcej z życia użytkownika tej właśnie metody. Spełniony w tym przypadku został jeden z podpuntów, kiedy warto stosować mocking: przypadek testowy trwa znaczny okres czasu zanim zwróci jakikolwiek wynik.
Czas najwyższy, aby na scenę wszedł wybrany przez nas framework do mockingu i abyśmy zrobili użytek z testów jednostkowych. Tworzymy projekt testów jednostkowych przy użyciu dowolnego środowiska - ja wybrałem darmowy MbUnit (http://mbunit.com), który mogę polecić.
Klasa testująca SmokeTest.cs


using System;
using MbUnit.Framework;

namespace MockingTaxiService.Tests
{
[TestFixture]
class SmokeTest
{
[Test]
public void TestShouldReturnTrue()
{
TaxiService svc = new TaxiService();
bool result = svc.Order(DateTime.Now.AddHours(5), "ul. Nieznana 12, Poznań", 1234);
// w przyp. sukcesu wynik powinien być typu: true
Assert.IsTrue(result);
}
}
}

Wykonajmy za ten pierwszy przypadek testowy, jego rezultat nie powinien być zaskoczeniem :-)






Test trwał długo - za długo aby mógł być wykonywany wraz z innymi. Tworzymy obiekt symulujący za pomocą RhinoMocks.
  1. Pobieramy pliki pakietu RhinoMocks ze strony: http://ayende.com/projects/rhino-mocks/downloads.aspx
  2. W projekcie testowym dodajemy referencję do pliku Rhino.Mocks.dll pobranego wcześniej pakietu RhinoMocks
  3. Zamieniamy ciało przypadku testowego stworzonego wcześniej, na nowe: korzystające z RhinoMocks:

using System;
using MbUnit.Framework;
using Rhino.Mocks;

namespace MockingTaxiService.Tests
{
[TestFixture]
class SmokeTest
{
MockRepository _mocks;

[SetUp]
public void initialize()
{
_mocks = new MockRepository();
}

[Test]
public void TestShouldReturnTrue()
{
var taxiService = _mocks.DynamicMock<ITaxiService>();
using (_mocks.Record())
{
SetupResult.For(taxiService.Order(DateTime.MinValue, null, 0)).IgnoreArguments().Return(true);
}

using (_mocks.Playback())
{
Assert.IsTrue(taxiService.Order(DateTime.Now, "ul. Nieznana 12, Poznań", 123));
}

}
}
}

Ok, przykład wymaga małego wyjaśnienia. Obiekt symulujący działanie tworzony zostaje przy pomocy:


var taxiService = _mocks.DynamicMock<ITaxiService>();

Jako parametr metody DynamicMock, której zadaniem jest dynamiczne stworzenie naszego "głupiego" obiektu jest interfejs bazowy obiektu, który będziemy symulować. W kolejmy fragmecie:


using (_mocks.Record())
{
SetupResult.For(taxiService.Order(DateTime.MinValue, null, 0)).IgnoreArguments().Return(true);
}

"nagrywamy" oczekiwane przez symulowany obiekt zachowanie. Uważny czytelnik zauważy, że przekazujemy do naszej metody Order puste parametry, gdyż one i tak nie będą brane pod uwagę przez środowisko testowe. Ignorowanie paramterów sygnalizujemy metodą IgnoreArguments() a po niej "na sztywno" podajemy jaki wynik zwóci nasza metoda: w tym przypadku true. Tak jak wcześniej pisałem kiedy tworzymy "głupi" obiekt, chcemy jedynie zasymulować działanie obiektu testowanego bez fizycznego zaprzęgania logiki biznesowej jaką w sobie zawiera.
Pozostaje nam teraz jeszcze wykonać test przy pomocy środowika klas testowych:



using (_mocks.Playback())
{
Assert.IsTrue(taxiService.Order(DateTime.Now, "ul. Nieznana 12, Poznań", 123));
}

Wynik testów jest zadowalający! Wystarczy sprawdzić, że test wykonał się błyskawicznie. Na tym zakończymy przygodę z mockingiem. Prawdą jest, że przedstawiony przypadek testowy to jedynie wzmianka o tym co potrafi cały framework RhhinoMocks oraz jego przyjaciel MbUnit. Zachęcam do przejrzenia dokuemntacji w/w frameworków.
Niech kod będzie za Wami!

niedziela, 26 października 2008

Mocking, a co to jest? odcinek I.

W dzisiejszym programie chciałbym przybliżyć nieco bliżej technikę Mocking. Czym jest mocking, do czego służy i czy można na tym zarobić? :-)
Mock (czyt. Mok), to specjalny obiekt tworzony w celu symulowania działania wybranych obiektów Twojej aplikacji. Stosuje się go głównie w technikach wytwarzania oprogramowania sterowanego testami jednnostkowymi, czyli TDD. Taki obiekt nie posiada w swojej implementacji pełnego zachowania obiektu, który naśladuje. No bo przecież inaczej po co miałby być wogóle tworzony. Jego zadaniem jest jedynie odpowiednie, ustalone reagowanie na jego wywoałania celem szybkiego testowania zachowania szerszego fragmentu systemu.
Dla jakich obiektów mocking jest najbardziej przydatny?
  • Obiekty, których stan trudno odtworzyć
  • Obiekty, których metody trwają znaczący czas (połączenia z bazami danych, zewnętrzne źródła danych)
  • Obiekty klas, których zależności z innymi obiektami są wysokie (trudno stworzyć instancję testowanego obiektu, gdyż posiada on rozległe zależności z resztą systemu)
  • Trudno przewidzieć wynik testowanej metody w trakcie testu (np. zwróć aktualną sekundę, aktualnej minuty, aktualnej godziny...)
Najpopularniejszymi środowiskami są: RhinoMocks (http://ayende.com/projects/rhino-mocks.aspx), Moq (http://code.google.com/p/moq/) i parę innych.
Przykład. Wyobraź sobie system internetowej rezerwacji taksówek. Podstawową funkcjonalnością systemu niech będzie możliwość złożenia zamówienia na określoną godzinę, określony adres - taksówki. Ok, nadal nie widzisz związku z tym co napisałem wyżej, spokojnie :-)
Główna metoda klasy TaxiService : ITaxiService:

public class TaxiService : ITaxiService
{
public bool OrderTaxi(DateTime toDateTime, string toAddress, int clientID)
{
//logika biznesowa
}
}
zanim złoży odpowiednią dyspozycję w systemie musi sprawdzić: dostępność taksówki, czas: czy jest możliwe przybycie auta na wybraną godzinę, odnaleźć dane klienta na podstawie jego numeru ID oraz sto innych rzeczy. Proces złożenia dyspozycji w samym systemie trwa np. ok 40 sekund, a jego wynikiem wartość boolowska.
40 sekund (albo i dłużej) to zbyt długo aby przeprowadzać na takim obiekcie testy: dobry test jednostkowy powinien być wykonany możliwie szybko i niezależnie od zewnętrznych danych, tak aby jego powtarzalność była możliwie prosta.
Wykorzystanie mockingu w tym przypadku polegać będzie na stworzeniu obiektu implementującego interfejs ITaxiService. Metoda Order nie będzie w takim obiekcie przetwarzała danych, zwróci jedynie wartość true, po to abyśmy mogli dalej testować system.

Tak pokrótce można scharakteryzować całą filozofię tego podejścia. W następnym odcinku postaram się wytłumaczyć mocking w przykładzie.

Console.WriteLine("Hello world!");

I wystartowałem.
Nazywam się Dariusz Tarczyński. Jestem programistą pasjonatą, programistą zawodowym oraz studentem technologii wytwarzania oprogramowania z Poznania.
Na blogu będę starał się przedstawiać głownie obszar moich zamiłowań, czyli platformy Microsoft .Net - pewnie ze szczególnym uwzględnieniem ASP .Net - bo w technologiach webowych najlepiej się czuję.
Ok, koniec bajdurzenia, do zobaczenia na blogu!