Zmiany, zmiany i jeszcze raz zmiany

Ile to już czasu minęło? Będzie z dwa miesiące jak nie więcej. Tak dłużej być nie może ;) pora wracać do żywych. Jednakże już nie na Zine, a w moim własnym nowym domku.

Podczas ostatnich miesięcy miałem w końcu czas by zająć się swoim prywatnym blogiem, wolno ale systematycznie, przenosiłem posty, bawiłem się CSSami oraz dziwiłem się programistą BlogEngine.NET dlaczego w niektórych miejscach stwierdzili że lepiej będzie jak zahardcodują wartość niż umożliwią jej edycję użytkownikowi. Po wielu zmaganiach udało się ;) i blog wydaje się, że działa dobrze :)

Od teraz komentarze na Zine zostały zablokowane (nie można ich zablkować, zablokowanie powoduje ukrycie wszystkich komentarzy), za to są odblokowane na nowym blogu gdzie można komentować również stare posty. Dodatkowo wszystkie nowe posty będą publikowane jedynie na nowym blogu, tak więc warto zapamiętać (jeżeli polubiliście mój blog;)) te oto trzy nowe linki:

·         Nowy Blog - http://blog.gutek.pl/

·         RSS Postów - http://feeds.feedburner.com/jakubg

·         RSS Komentarzy - http://feeds.feedburner.com/jakubg-comments

W razie jakichkolwiek problemów z blogiem proszę o kontakt, dzięki!

To jedna z pierwszy zmian, inna to ostatnie dni tak zwanego Death March Project! Wyobrażacie sobie wakacje bez weekendów? Ja nie muszę bo to mniej więcej tak wyglądało :/ Jednakże muszę przyznać, że wraz zespołem wykonaliśmy kawał dobrej roboty a przy tym można było się nieźle pośmiać a i czasami nawet napić ;) Jeszcze tylko tydzień i fanfary będą grać na lewo i prawo! Trzymajcie kciuki :)

Do tego wszystkiego MS uruchomił program wczesnej adopcji SharePoint 2010, więc w końcu będę mógł o nim pisać nie łamiąc przy tym żadnych NDA. Zapowiada się ciekawie, pytanie tylko czy wypuszczą VS SharePoint Tools dla 2010 czy też będą woleli poczekać ;) oraz czy firma da mi wolną rękę w zabawie z nowym produktem :)

W tym wszystkim co się działo w ogóle nie miałem czasu dla PGS, i mam wrażenie że zawiodłem w tym temacie. IMO jedyny wyjściem jest naprawienie tej całej sytuacji i obiecuje że od przyszłego tygodnia będą myślał jak to zrobić w szczególności iż szykują się zmiany w MS i trzeba będzie kilka rzeczy dogłębnie przemyśleć. Tym czasem jeżeli jest ktoś chętny do pomocy w prowadzeniu warszawskiej grupy PGS dajcie znać z chęcią przyjmę każdą pomoc gdyż sam nie będę się wyrabiał :(

Kończąc chciałbym wszystkim polecić książkę Made to Stick: Why Some Ideas Survive and Others Die nie będę sie w dawał w jej opis gdyż Amazon zawiera naprawdę bardzo dobre i szczegółowe jej recenzje, więc zachęcam do ich przeczytania. W Polsce dostałem ją w American Book Store więc jeżeli aktualnie nie macie co czytać, polecam, warto.

opublikowano 28 sierpnia 09 01:11 przez Gutek | 6 komentarzy   
Zarejestrowano w kategorii:
Tips & Tricks 15: Lorem ipsum dolor w MS Word 2007?

Nie ma problemu ;) jest bowiem fajny feature, jeżeli w MS Word 2007 (nie mam innego by sprawdzić) wpiszecie:

=lorem()

i następnie wciśniecie enter, to wpisany tekst zostanie zamieniony na coś takiego:

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, sit amet commodo magna eros quis urna.

Nunc viverra imperdiet enim. Fusce est. Vivamus a tellus.

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Proin pharetra nonummy pede. Mauris et orci.

Dobre nie? Dodatkowo, jeżeli chcecie to możecie podać wartości tak:

=lorem(libcza_paragrafów, liczba_wierszy_w_paragrafie)

liczba_paragrafów, określa liczbę wystąpień paragrafu zawierającego liczba_wierszy_w_paragrafie, przez co tekst wygenerowany będzie ciutkę inaczej wyglądał niż ten domyślny z funkcji bez parametrów :)

A tak długo się zastanawiałem skąd ludzie biorą te wartości ;) He he

opublikowano 25 czerwca 09 01:14 przez Gutek | 4 komentarzy   
Zarejestrowano w kategorii: ,
Trzy problemy i przysłowiowy MacGyver

Wczoraj był dzień MacGyvera, z rana zostałem wysłany do jednego klienta by rozwiązać tak naprawdę jeden problem, potem miałem mieć spotkanie w pracy ale spędziłem je przed telefonem w celu rozwiązania kolejnego problemu a na dowidzenia dostałem jeszcze jeden ;)

Zacznijmy od pierwszego.

Polecenie BULK INSERT nie może dostać się do pliku lub go odczytać.

Przyjechałem na miejsce gdzie na komputerze X został udostępniony katalog z plikiem, który ma zostać odczytany przez BULK INSERT a następnie przetworzony. Dostałem także informacje, że SQL Server stoi na clustrze. Błąd zwrócony był dość dziwny:

Cannot bulk load because the file "\\X\folder\file1.cvs" could not be opened. Operating system error code 5(error not found).

Jak się przeszuka Google pod względem tego błędu to wyskoczy wiele propozycji – złe uprawnienia, netpipes włączone, wyłączona jakaś konfiguracja w SQL Server i wiele wiele innych. Było ich aż tak dużo, że musiałem skontaktować się z Brejkiem bo zaczynałem głupieć. Oczywiście moje wytłumaczenie problemu było bym powiedział kiepskie co zresztą potem stwierdziliśmy. Jednak to co dało mi do myślenia po rozmowie to słowo Node, które użył Brejk. No więc (nigdy nie rozpoczynaj zdania od Więc ;)) przypomniałem sobie, że SQL Server stoi na clustrze. Skoro to cluster to istnieje jakiś wirtualny komputer, który po w sobie różne nody, jak się okazało to komputer X był jednym z nodów. Wystarczyło więc zobaczyć jakie zasoby są współdzielone przez cluster, i następnie stworzyć share z odpowiednimi uprawnieniami na dysku współdzielonym na komputerze wirtualnym i boom :) BULK INSERT zaczął działać.

Morał z tego jest taki: zanim zaczniemy cokolwiek robić należy sprawdzić, jaka jest konfiguracja aktualna sieci i komputerów. Co mi z tego, że spędziłem 3h rozwiązując ten problem, jak problem był błahy i nie powinien zająć więcej niż 10 minut. Należy też zapamiętać, clustry mają komputer wirtualny, na którym jest narządko CluAdmin.exe jak dobrze pamiętam podające, jakie zasoby dla określonych grup są współdzielone. I następnie albo należy stworzyć nowy zasób albo na istniejącym zasobie stworzyć share.

Problem drugi na szczęście udało się rozwiązać telefonicznie.

Ludzie należący Domain Local Group nie mogą się zalogować do aplikacji, błąd: Missing Role.

Na początku wytłumaczę błąd Missing Role, gdyż jest on specyficzny dla aplikacji. Nasza aplikacja łączy Windows Authentication i Forms Authentication na jednej stronie dzięki nowej funkcjonalności IIS 7. Forms Authentication ma custom membership provider, który autoryzuje użytkownika z domeną. Czyli Windows Auth służy by użytkownik ordazu się zalogował zaś Forms wspomaga użytkownika jakby chciał się na innego użytkownika zalogować. Teraz by jakikolwiek użytkownik mógł korzystać z aplikacji musi należeć do jednej z 20 grup AD. Grupy zostały nam narzucone wraz z nazewnictwem i okazało się, że część druga nazwy grupy po podkreśleniu określa rolę jaką użytkownik ma mieć w systemie. Teraz, jeżeli nie można pobrać grupy AD z obiektu użytkownika to zwracany jest błąd Missing Role.

Sutacja była następująca, osoba z domeny X.pl w której Domain Local Group o roli Z była stworzona mogła się zalogować, zaś osoba z domeny Y.X.pl dostawała Missing Role.

Pierwszą rzeczą, jaka może się nasuwać to błąd w kodzie, jednak nie tym razem. Napisałem taka małą aplikację windowsową testująca wyszukiwanie po AD za pomocą naszego kodu. Sprawdziliśmy na początku, jakie grupy są pobierane dla osoby z domeny X.pl, a następnie z domeny Y.X.pl. Okazało się, że wszystkie grupy dobrze są pobierane dla obydwóch użytkowników, z mała różnicą. Użytkownik z domeny Y.X.pl nie miał informacji o grupie Domain Local Group. Za to grupa miała informacje o tym iż użytkownik z domeny Y.X.pl przynależy do niej. Czyli coś nie grało… okazało się, że problem w tkwi w typie grupy AD. Domain Local Group może zawierać użytkowników ze wszystkich domen i lasów, jednakże jest widoczna tylko i wyłącznie w domenie, w której została utworzona. Czyli domena Y.X.pl nie widzi informacji o grupie Domain Local Group z domeny X.pl. Zamiana na typ grupy Universal załatwiła sprawę i nagle wszyscy użytkownicy mogą się logować do aplikacji.

Morał: Poznaj dokładnie strukturę AD u klienta, by nagle nie wpakować się w bagno z powodu nie przewidywalnych okoliczności. Należy wiedzieć, z jakich domen użytkownicy mogą się logować, na jakich komputerach stoją domeny – czy są tam domeny 2000? Czy istnieją inne lasy, czy użytkownicy z innego lasu mają się logować do naszej aplikacji w drugim lesie. Itp. itd. ważne jest znanie na te pytania odpowiedzi, bo na przykład jak klient powie, że chce mieć kerberosa działającego pomiędzy domenami z ustawionym trustem, zaś jedna domena stoi na win2k a druga na win2k3 to możemy mu od razu powiedzieć, że tego się nie da zrobić do póki win2k nie zostanie podniesiona do minimum win2k3.

Trzeci ostatni błąd, nierozwiązany… jeszcze :)

Połączenie do Analysis Services za pomocą Connection String łączy się jako Anonymous user.

Tutaj dużo można gadać, nie licząc, że cała architektura i konfiguracja środowiska jest chyba jedną z największych porażek, jakie widziałem, ale skoncentruje się na tym jednym problemie.

Mamy aplikację WEB, która łączy się z usługami sieciowymi, które działają na usłudze Windows (usługa Windows je tworzy), komunikacja idzie po WCF, zaś usługa Windows udostępnia API do łączenia się z bazami danych plus także z Analysis Services. Teraz w connection string do analysis services ustawione są dwie właściwości User Id i Password, user Id zawiera użytkownika domenowego zaś Password jego hasło (blah hasło użytkownika domenowego plain textem… i to w pliku… nie no super architektura nie ma co). I teraz ta konfiguracja działała do zeszłego tygodnia, od zeszłego tygodnia (podobno zmian nie było) nagle połączenie do Analysis Services próbuje łączyć się jako Anonymous user… zaś jeżeli wykorzystamy ten sam connection string w innej domenie to… wszystko działa poprawnie. Jeżeli wykorzystamy zmodyfikowany connection string bez user ID i password i odpalimy aplikację na dedykowanym koncie to connection string działa, odpalimy na tym koncie, na którym cała usługa Windows chodzi, i nie działa. Ciekawy błąd choć ja najpierw bym poprawnie skonfigurował całe środowisko a nie wykorzystywał wszędzie Network Service, Local System i Anonymous user bo to po prostu jak podrzynać sobie żyły krakersem… bezsensowne prawda? :)

Morał: konfigurujcie dobrze swoje środowiska to przynajmniej jak coś padnie będziecie wstanie to szybko z diagnozować :)

opublikowano 17 czerwca 09 01:01 przez Gutek | 9 komentarzy   
Zarejestrowano w kategorii: , ,
Unable to open the physical file XXX.mdf

Dostałem od znajomego bazę danych, którą chciałem podłączyć do SQL Server za pomocą opcji Attach:

Niestety, wszystkie próby na Windows 7 kończyły się komunikatem:

TITLE: Microsoft SQL Server Management Studio

------------------------------

Attach database failed for Server 'GUTEK-NOTEBOOK'.  (Microsoft.SqlServer.Smo)

For help, click: http://go.microsoft.com/fwlink?ProdName=Microsoft+SQL+Server&ProdVer=10.0.2531.0+((Katmai_PCU_Main).090329-1015+)&EvtSrc=Microsoft.SqlServer.Management.Smo.ExceptionTemplates.FailedOperationExceptionText&EvtID=Attach+database+Server&LinkId=20476

------------------------------

ADDITIONAL INFORMATION:

An exception occurred while executing a Transact-SQL statement or batch. (Microsoft.SqlServer.ConnectionInfo)

------------------------------

Unable to open the physical file "D:\_Projects\VCardStore\VCard.mdf". Operating system error 5: "5(failed to retrieve text for this error. Reason: 15105)". (Microsoft SQL Server, Error: 5120)

For help, click: http://go.microsoft.com/fwlink?ProdName=Microsoft+SQL+Server&ProdVer=10.00.2531&EvtSrc=MSSQLServer&EvtID=5120&LinkId=20476

Linki niestety nie dają za dużo informacji a sam opis błędu jest… beznadziejny, osobę, która taki opis wymyśliła powinni wrzucić na kurs komunikacji między ludzkiej, bo błąd może i maszyna zrozumie ale człowiek? To tak jak pomocy technicznej udzielić odpowiedzi na pytanie „Co nie działą?” – „Aplikacja!” :)

Rozwiązanie okazało się beznadziejnie proste (jak już się człek zorientował co może być nie tak… co już nie było takie proste), ale i też głupie, należało otworzyć MS SQL Server Management Studio z prawami administratora :) i Attach już działał poprawnie :)

opublikowano 13 czerwca 09 03:11 przez Gutek | 18 komentarzy   
Zarejestrowano w kategorii: ,
OOT: RegEx

Ostatnio natrafiłem na piękne wyrażenie RegEx, które… podsumowuje nasze życie :)

^(Birth){1}([0-9]+\sYears)(Happiness)+?(Death)*?$

PS.: OOT

opublikowano 04 czerwca 09 02:20 przez Gutek | 4 komentarzy   
Zarejestrowano w kategorii:
Code Contracts - {P} C {Q} (Aktualizacja: 09-06-04)

Aktualizacja 2009-06-04 (oryginał z 2009-03-24 01:17). To, co mnie zawsze denerwuje w postach w sieci to to iż 30% z nich odwołuje się do rzeczy, które nie istnieją gdyż od wersji beta/ctp/rc uległy zmianie. Dlatego też stwierdziłem, iż zaktualizuje ten post o kilka drobnych zmian, które weszły w życie 19 maja 2009 roku. Na przekreślone czerwono rzeczy, które zostały usunięte od ostatniej wersji postu. Na zielono rzeczy, które zostały dodane od ostatniej wersji postu. Na czarno rzeczy, które nie uległy zmianie od ostatniej wersji postu.

Wstęp

Od kiedy zacząłem programować zawsze miałem ten sam problem, jak efektywnie oprogramować walidację parametrów metody? Czy czegoś nie pominąłem? Czy dokumentacja jest aktualna? Ostatnio nawet Nuwanda stworzył plugin dla R#, którego celem jest ułatwienie zarządzania dokumentowaniem wyjątków w kodzie.

Jednak tak jak i moja pierwsza przygoda z Mac tak też zakończyła się moja przygoda z R#. Od razu dostałem critical error, zamknąłem szczękę i stwierdziłem, że tego używać nie będę ;) Tak, dla niewierzących, dostałem taki błąd w R#, Procent świadkiem bo mu stack trace przesłałem :)

W czasie, kiedy coraz częściej piszemy kod, który jest używany przez kolejne osoby i w kolejnych projektach musimy więcej czasu poświęcić na to by poprawnie obsłużyć wyjątki, walidację, weryfikację a na końcu lub na początku błędy.

Jest to tym trudniejsze im większy jest projekt, im więcej zmian w nim robimy. Kto się spotkał z sytuacją że XML Documentation zwracała mu ponad 500 błędów/braków? Nawet kiedy piszę się dokumentację od razu, to potem nagle trzeba coś na szybko zmienić i nie aktualizujemy dokumentacji. Miło, że mamy takie toole jak R# czy nawet weryfikator dokumentacji w VS, ale one za nas nic nie zrobią, tylko poinformują – kliknij tam by coś poprawić.

Oczywiście, możemy zawsze skorzystać z GhostDoc, ale to znów wymaga klikania przez nas i aktualizacji dokumentacji w zależności od zmian w kodzie.

Także dość często sygnatura naszej metody ulega zmianie, dochodzą nowe parametry, które trzeba przetestować, albo zmienia się ich wymaganie co do wartości jakie mogą przyjmować. Czy mieliście sytuację, iż początek naszej metody to 8 ifów sprawdzających czy ciąg znaków jest pusty? A czy klient wie że każdy z tych parametrów jest przez was weryfikowany?

Czy wywołując metodę zastanawialiście się jakie wartości ona może wam zwrócić? Czy aby na pewno to jest ta metoda, którą wy chcecie wywołać?

Ja osobiście miałem dużo takich sytuacji i sam nie raz się głowiłem jak mam opisać/przekazać klientowi co dokładnie robi metoda by potrafił on z niej od razu skorzystać a nie brał za słuchawkę i dzwonił, lub pisał na forum pytania o „tego autora który napisał tą książkę w zeszłym roku, po tym jak jedna z jego ekranizacji dostała nagrodę Emmy”.

Tutaj właśnie znajdują zastosowania kontrakty! A ich implementacja wykonana przez MS, zezwoli nie tylko na utrzymanie poprawnej dokumentacji wraz z informacją jakie wartości może metoda przyjmować ale także jakie wartości ona zwróci nam kiedy spełnimy warunek początkowy – poprawny parametr. Nie trzeba pisać dokumentacji, a ni nic. Wystarczy, że określimy warunki brzegowe.

Jeżeli jesteśmy leniwi ;) to można włączyć analizator statyczny, który od razu nas poinformuje, że w danej metodzie przydałoby się stworzyć walidację gdyż na tym właśnie kontrakt polega –na formalnej weryfikacji i specyfikacji.

A dzięki integracji z PEX, testowanie kodu będzie czystą przyjemnością. Kontrakt określi nam, jakiego typu parametrów się spodziewa, więc PEX może łatwo sprawdzić czy inne parametry nie spowodują błędnego wykonania kodu, jak i czy ten parametr, który oczekujemy spełnia nasz wyjściowy warunek.

Dlaczego o tym piszę i dlaczego wstęp zajmuje aż stronę :) A to dlatego iż coraz więcej jest technologii a coraz mniej informacji do czego ona się może przydać. Nawet ostatnio słyszałem opinie o kilku fajnych produktach, które może wyjdą, ale mają jeden wielki problem, nawet architekt danego produktu nie wie do czego go można wykorzystać bo słowo „wszystkiego” nikogo niezadowala ;)

Co to jest – Code Contracts?

Code Contracts a dokładnie Design By Contract (nazwa zastrzeżona przez Interactive Software Engineering, właściciela języka Eiffel) to metodyka tworzenia API w sposób formalny, dokładny oraz weryfikowalny. Działa ona głownie na zasadzie logiki Hoara, która określić można za pomocą wyrażenia {P} C {Q} gdzie P (pre-condition) jest warunkiem który musi być spełniony by C (command) mogło zajść i zakończyć swoje działanie tak by przeszło weryfikacje Q (post-condition). Czyli mówiąc językiem bardziej zrozumiałym, jeżeli chcemy zarezerwować pokój w hotelu, to:

1)      Pre-condition: Musimy mieć kartę kredytową by móc zarezerwować pokój;

2)      C: Hotel wykonuje rezerwację;

3)      Q: Hotel zarezerwował nam pokój a liczba wolnych pokoi zmniejszyła się o 1.

Czyli, pre-condition, to jest to co klient korzystający z naszego API musi spełnić. Command to coś co my wykonujemy by spełnić post-condition. Jeżeli w jakimkolwiek przypadku nastąpi naruszenie zasady, zostanie przekazana nam informacja zwrotna, na przykład: brak miejsc, karta nie ważna, critical error ;)

Dodatkowo kontrakty, oprócz logiki Hoara, dostarczają nam tak zwane inwarianty. Inwariant jest to warunek, który musi być prawdziwy zarówno na początku jak i na końcu. Wracając do naszego przypadku, inwariantem przy rezerwacji pokoju hotelowego może być istniejący budynek hotelu. Musi on istnieć w trakcie naszej rezerwacji jak po jej zakończeniu, gdyż w przeciwnym wypadku nie moglibyśmy w naszym pokoju zamieszkać.

Wikipedia, trochę bardziej się rozpisuje na temat Design By Contract, dodając do logiki Hoara i inwariantów, formalną weryfikację i formalną specyfikację – czyli tak naprawdę opisanie P i Q w trochę bardziej zawiły sposób ;)

Dodatkowo, jak sama Wikipedia wskazuje a ja się z tym zgadzam ;) DbC odróżnia się od programowania defensywnego tym iż w DbC wszystko jest jasne, klient wie co ma przekazać by dostawca mógł wykonać operację i zwrócić klientowi to czego on się spodziewa.

Jak działają Code Contracts w .NET?

No dobrze, to tyle słów wstępu na temat co to w ogóle jest ;) Pora przejść do tego co MS stworzył i to co jest już dostępne dla .NET Framework 3.5, zaś co będzie częścią .NET Framework 4.0. Mowa oczywiście o Code Contracts! ;) Tak jakbym już o tym nie mówił ;)

.NET Framework 4.0 będzie zawierał zaimplementowane kontrakty bezpośrednio w mscorlib.dll, zaś aktualnie jeżeli z tego chcemy skorzystać to Microsoft Research opublikował dla nas bibliotekę, którą możemy załączyć do projektu i z niej skorzystać. Występuje ona w dwóch różnych wersjach:

1.       Wersja akademicka, bez licencji na tworzenie oprogramowania komercyjnego;

2.       Wersja z licencją na tworzenie oprogramowania komercyjnego.

Jest to pakiet MSI, który nie tylko instaluje bibliotekę ale także integruje się z Visual Studio, dodając nam nową zakładkę w właściwościach projektu.

To co uzyskujemy po zainstalowaniu MSI to:

·         Biblioteka Microsoft.Contracts.dll, którą musimy załączyć do projektu w celu możliwości korzystania z kontraktów (występuje na zakładce .NET więc nie trzeba jej szukać za pomocą Browse, nazywa się Microsoft Contracts Library, zaś znajduje się w przestrzeni nazw System.Diagnostic.Contracts);

·         Integracja z ustawieniami projektu, dzięki czemu możemy włączyć lub wyłączyć analizę pod względem poprawności DbC w naszym kodzie;

·         Analizator statyczny kodu weryfikujący kod, który piszemy by był zgodny z zasadami DbC – analiza wykonywana jest w trakcie kompilacji;

·         Analizator run-time, który działa w trakcie uruchomienia aplikacji.

W planach zaś twórcy Code Contracts chcą dodać automatyczne generowanie dokumentacji w zależności od użytych kontraktów w kodzie i lepszą integrację z intellisense (tak by jak będziemy korzystać z API od razu pojawiała nam się informacja, że znów coś źle robimy;)).

Zanim jeszcze może przejdę do omawiania już czystego Code Contracts, warto wspomnieć o tym, iż sam projekt wywodzi się z języka Spec#, stworzonego przez Microsoft Research (mowa o CC, DbC wywodzi się od języka Eiffel). Jest to tak naprawdę C#, który został rozszerzony o kilka dodatkowych słów kluczowych, konstrukcji językowych, nie nullowalnych typów danych itp. w celu dostarczenia kontraktów bezpośrednio w języku. Dzięki czemu grupa MR miała dobre pole do trenowania i wyciągania wniosków, co może się sprawdzić a co nie, jak coś zaimplementować i czego nie implementować, bo mija się z celem. W C# 4.0 to nie zostało zaimplementowane ze względów dość zrozumiałych, nie ma sensu tego dostarczać per język, lepiej dostarczyć środowisko/platformę, którą każdy język (VB.NET, C#, J# itp.) będzie mógł wykorzystać.

Taka małą uwaga, nie instalujcie Spec# i Code Contracts jednocześnie, ich zakładki do właściwości projektów się gryzą i ani to, ani to poprawnie działać nie będzie :)

No dobrze, koniec :) pora na trochę szczegółów :)

CC składa się z metod statycznych, nie tak jak by większość mogłaby przypuszczać z atrybutów, MR zdecydował się na taką implementację ze względu na:

·         Wsparcie IDE – gdyby zastosowali kontrakty jako atrybuty nie dałoby się w trakcie pisania tworzyć odpowiedniego intellisense dla metod;

·         Parsowanie danych – własne atrybuty mają ograniczenie, co do wartości, jakie mogą być tam przekazywane, co prowadzi w końcu do zapisania wartości, jako ciągi znaków, które potem muszą być parsowane, przez co duplikuje się funkcjonalność którą kompilator już posiada;

·         Wsparcie run-time – bez wsparcia aplikacji do przepisywania kodu binarnego, atrybuty nie mogą być weryfikowane w trakcie działania aplikacji, przez co jeżeli byśmy chcieli by nasz kontrakt był weryfikowany w trakcie działania aplikacji, musielibyśmy albo z duplikować funkcjonalność kontraktu w kodzie, albo stworzyć aplikację do przepisywania kodu binarnego.

No dobrze, skoro nasze kontrakty są metodami statycznymi to jak z nich korzystamy? Dość prosto, zaraz po definicji metody umieszczamy odpowiednie wywołanie metody z CC. Przykładowy kontrakt sprawdzający czy parametr przekazany do metody jest większy niż 10 i mniejszy niż 20 a parametr zwracany z metody mniejszy niż 10, wygląda następująco:

public int Test(int a)

{

    Contract.Requires(a > 10);

    Contract.Requires(a < 20);

    Contract.Ensures(Contract.Result<int>() < 10);

 

    a -= 10;

 

    return a;

}

Jak zauważyliście kontrakty nie zależnie od tego czy są pre czy post, są deklarowane na początku metody. To co robi MR, to bierze kod napisany i skompilowany do postaci IL i wykonuje na nim operacje przepisania, przestawiając tak post-conditions by były one umieszczone na końcu kodu. Powyższy kod w .NET Reflector wygląda następująco:

public int Test(int a)

{

    __ContractsRuntime.Requires(a > 10, null, "a > 10");

    __ContractsRuntime.Requires(a < 20, null, "a < 20");

    a -= 10;

    int CS$1$0000 = a;

    int Contract.Result<int>() = CS$1$0000;

    __ContractsRuntime.Ensures(Contract.Result<int>() < 10, null, "Contract.Result<int>() < 10");

    this.ObjectInvariant();

    return Contract.Result<int>();

}

Czyli nasze pre-conditions zostały zamienione na wywołanie metody Requires, która sprawdza pierwszy parametr, który jeżeli nie zakończy się jako true to zostaje zwrócony błąd z opisem po prawej stronie. Zaś nasz zwrot return został zamieniony na kilka dodatkowych linijek, pierwsza tworzy zmienną tymczasową, która przypisuje do „wyniku” kontraktu wartość zwracaną. Wynik kontraktu jest następnie weryfikowany i jeżeli on przejdzie weryfikacje sprawdzany jest inwariant. Na końcu zwracana jest wartość, którą my zwrócilibyśmy bez weryfikacji.

Plus rozwiązania tego za pomocą przepisywania kodu, jest taki iż możemy sami sobie napisać naszą własną metodę, która będzie za to odpowiedzialna, czyli jeżeli chcemy stworzyć różne buildy w różnych systemach, możemy wykorzystać nasz własny „przepisywacz” by spełnić warunki wymagane przez oprogramowanie firmy trzeciej – np.: nie wyrzucać wyjątków a jedynie wypisać je na konsole:

public static class RuntimeRewriterMethods

{

    [DebuggerStepThrough]

    public static void Requires(bool cond, string userMsg, string condText)

    {

        if (!cond)

            Console.WriteLine("no nie... chyba nikt już nic nie czyta");

    }

 

    // nowa metoda

    [DebuggerStepThrough]

    public static void Requires<TException>(bool cond, string userMsg, string condText) where TException : Exception

    {

        if (!cond)

            Console.WriteLine("no nie... chyba nikt już nic nie czyta");

    }

 

    [DebuggerStepThrough]

    public static void Ensures(bool cond, string userMsg, string condText)

    {

        if (!cond)

            Console.WriteLine(condText);

    }

    [DebuggerStepThrough]

    public static void Assert(bool cond, string userMsg, string condText)

    {

        if (!cond)

            Console.WriteLine(userMsg);

    }

    [DebuggerStepThrough]

    public static void Assume(bool cond, string userMsg, string condText)

    {

        if (!cond)

            Console.WriteLine("zakladanie czegos konczy sie wlasnie o TAK");

    }

    [DebuggerStepThrough]

    public static void Invariant(bool cond, string userMsg, string condText)

    {

        if (!cond)

            Console.WriteLine("a to mialo byc prawda i tylko prawda");

    }

}

Tak naprawdę to, co robi VS po tym jak z builduje normalnie naszą aplikację to wykonuje polecenie:

"C:\Program Files\Microsoft\Contracts\Bin\ccrewrite" /rewrite  "/libpaths:C:\Program Files\Microsoft\Contracts\Contracts" "C:\Users\Gutek\Desktop\Samples\ApiProtocols\bin\Debug\ApiProtocols.exe"

Nie będę wchodził w szczegóły – na końcu dam kilka źródeł skąd można przeczytać więcej na ten temat. Chodziło mi tylko o to by pokazać iż jest to „przepisywacz” :)

Koniec „teorii” pora na trochę praktyki. Nie będzie jej tutaj aż tak dużo jakby można przypuszczać a to ze względu na to iż źródła na które was skieruje zajmują 25 stron A4 łącznie i po ich przeczytaniu :) będziecie masta :)

To co IMO jest najważniejsze to opanowanie metod, które opisałem poniżej.

Requires – tak zwane pre-condition

Za każdym razem kiedy chcemy by do naszej metody były przekazywane określone parametry, spełniające nasze oczekiwanie piszemy:

Contract.Requires(condition);

Contract.Requires(condition, "error message");

Nasz warunek jest to dowolne wyrażenie zawierające parametr metody i zwracające true lub false. W przypadku ewaluowania wyrażenia do false zostanie nam zwrócony błąd w postaci domyślnej (przykład a < 20), lub dowolnej jaką przedstawimy jako ciąg znaków.

Requires<TException> – tak zwane pre-condition z własnym wyjątkiem NOWE

Różnica pomiędzy Requires a Requires<TException> jest taka, iż deklarujemy typ wyjątku, jaki chcemy wyrzucić:

Contract.Requires<ArgumentNullException>(condition);

Contract.Requires<ArgumentNullException>(condition, "param name");

Nasz warunek jest to dowolne wyrażenie zawierające parametr metody

RequiresAlways – mhhhh :) deprecated

Prawie niczym się nie różni od Requires. Dosłownie mówiąc wykonuje to samo dla każdego typu kompilacji i tu jest właśnie pies pogrzebany :) W zależności od ustawień kompilacji Requires będzie brany pod uwagę lub nie, zaś RequiresAlways zawsze będzie brany :) Należy więc z niego korzystać rozsądnie :)

Ensures – nasze Q (post-condition)

Tak jak możemy pisać pre-conditions za pomocą Requires lub RequiresAlways, tak nasze post-conditions piszemy wykorzystując metodę Ensures:

Contract.Ensures(condition);

Contract.Ensures(condition, "error message");

Tak jak w Requires, warunek musi być spełniony by nie został zwrócony błąd.

Dla Ensures, dostępne są dodatkowe metody, które możemy wywołać podczas wykorzystania warunku:

·         Contract.Result<T>() – zawiera wartość zwracaną z metody, jeżeli chcemy sprawdzić czy wartość zwracana spełnia jakiś warunek to dzięki tej metodzie możemy to zrobić. T oznacza typ zwracanych danych. Oczywiście kiedy nic nie zwracamy, nie możemy skorzystać z tej metody ;)

·         Contract.OldValue<T>(T value) – umożliwia weryfikację czy poprzedni stan danej wartości jest równy/nie równy nowemu stanowi, dla przykładu modyfikujemy własność klasy A tak by była równa B, a B równa A, więc to co my możemy zrobić to napisać kod Contract.Ensures(Contract.OldValue<int>(this.A) == this.B && this.A == Contract.OldValue<int>(this.B));

·         Contract.ValueAtReturn<T>(out T value) – w przypadku kiedy nasza metoda korzysta z parametru wyjściowego, możemy wykożystać ValueAtReturn by sprawdzić czy parametr wyjściowy będzie zawierał odpowiednią wartość. W tym przypadku ani OldValue ani Result nie zadziała, jedyną opcją sprawdzenia parametru wyjściowego jest wykonanie metody ValueAtReturn.

EnsuresOnThrow – nasze Q (post-condition) dla wyjątków

Czasami niezależnie jak bardzo się staramy mogą wystąpić różne wyjątki. Możemy na przykład korzystać z biblioteki firmy trzeciej, która z kontraktami ma tyle wspólnego, co reklama mortadeli z Mango. Jednak dostawca był na tyle miły, iż powiedział nam, że może on wyrzucić takie wyjątki mimo, że operacja zakończyła się poprawnie. Za pomocą EnsuresOnThrow możemy zagwarantować, żeby nasz post-condition był spełniony mimo wystąpienia błędu TException:

Contract.EnsuresOnThrow<TException>(condition)

Contract.EnsuresOnThrow<TException>(condition, "error message")

Korzystanie jednak z tej metody niesie za sobą niebezpieczeństwa – TException dotyczy wszystkich wyjątków wraz z uwzględnieniem dziedziczenia, czyli łapanie Exception może spowodować wychwycenie takich wyjątków jak StackOverflowException czy RuntimeException, które np.: nie powinny być przechwycone. W EnsuresOnThrow stosujemy tą samą zasadę jak przy try/catch/finally – łapiemy szczegółowo a nie ogółowo :)

EndContractBlock – kompatybilność wstecz :)

Tak jak wspomniałem już we wstępnie. Do tej pory pisaliśmy zawsze nasze ukochane ify:

if(a <= 10 || a >= 20)

{

    throw new ArgumentOutOfRangeException("a", a, "10 < a < 20");

}

Wraz z wprowadzeniem kontraktów, pisanie ich… mija się z celem, ale co zrobić kiedy mamy je już popisane i nie chcemy tego kasować bo ktoś stworzył CC? MR dostarczyli nam metodę EndContractBlock, którą stosujemy po wszystkich naszych ifach a która zamienia nasze ify na kontrakty, czyli to co mamy powyżej zapisujemy teraz tak:

if(a <= 10 || a >= 20)

{

    throw new ArgumentOutOfRangeException("a", a, "10 < a < 20");

}

Contract.EndContractBlock();

To co uzyskujemy w .NET Reflector to:

bool _preConditionHolds;

if ((a <= 10) || (a >= 20))

{

    _preConditionHolds = false;

}

else

{

    _preConditionHolds = true;

}

if (!_preConditionHolds)

{

    throw new ArgumentOutOfRangeException("a", a, "10 < a < 20");

}

Jest jednak pewna zasada, której trzeba się trzymać inaczej „kompatybilność wstecz” nie zadziała.

1.       Po pierwsze w naszej metodzie nie może być nic wcześniej niż nasze ify;

2.       Po drugie ify nie mogą mieć klauzuli else;

3.       Po trzecie ify muszą się kończyć throw new TException;

4.       Po czwarte na końcu ifów a przed kodem musimy dodać EndContractBlock().

ObjectInvariants – coś co zawsze jest prawdziwe

Ze względu na to iż nie ma atrybutów, a pisanie inwariantów notorycznie w każdej metodzie w której korzystamy z kontraktów mijałoby się z celem, został wprowadzony mechanizm określania wartości które zawsze muszą być prawdziwe:

[ContractInvariantMethod]

protected void ObjectInvariant()

{

    Contract.Invariant(_a != 10 || _a != 20);

}

Na początku definiujemy metodę zwracającą void i określoną atrybutem ContractInvariantMethod. Atrybut ten informuje kompilator, iż ta metoda odpowiedzialna jest za przechowywanie wszystkich wartości, które zawsze powinny spełniać określony warunek. Podobnie jak w Ensures i Requires, Contract.Invariant może wyglądać następująco:

Contract.Invariant(condition);

Contract.Invariant(condition, "error message");

A jak pamiętacie przykład, na jakiej zasadzie działa „przepisywacz” kodu to zapewne zauważyliście wywołanie metody ObjectInvariant:

public int Test(int a)

{

    __ContractsRuntime.Requires(a > 10, null, "a > 10");

    __ContractsRuntime.Requires(a < 20, null, "a < 20");

    a -= 10;

    int CS$1$0000 = a;

    int Contract.Result<int>() = CS$1$0000;

    __ContractsRuntime.Ensures(Contract.Result<int>() < 10, null, "Contract.Result<int>() < 10");

    this.ObjectInvariant();

    return Contract.Result<int>();

}

I tak oto CC załatwia nam sprawę walidacji inwariantów :)

Interface i Abstract

To już ostatni punkt zabawy z CC. Ze względu na to iż C# i inne języki nie zezwalają na przykładowe implementacje interfejsów jak i klas abstrakcyjnych, MR wprowadziło dwa atrybuty:

·         [ContractClass(typeof(ClassName))] – atrybut określający klasę, która zawiera implementację danego interfejsu lub klasy abstrakcyjnej;

·         [ContractClassFor(typeof(InterfaceOrAbstractClassName))] – atrybut określający nazwę interfejsu/klasy abstrakcyjnej, której implementacja kontrkatu znajduje się w klasie oznaczonej tym atrybutem.

Przykład (zaczerpnięty z dokumentacji):

[ContractClass(typeof(IFooContract))]

interface IFoo

{

    int Count { get; }

 

    void Put(int value);

}

 

[ContractClassFor(typeof(IFoo))]

sealed class IFooContract : IFoo

{

    int IFoo.Count

    {

        get

        {

            Contract.Ensures(0 <= Contract.Result<int>());

            return default(int);

        }

    }

    void IFoo.Put(int value)

    {

        Contract.Requires(0 <= value);

    }

}

Od tej pory, każda klasa implementująca dany interfejs będzie podlegała weryfikacji kontraktu :)

Integracja z VS

Stwierdziłem, że warto opisać tą zakładkę gdyż głowienie się co oznaczają poszczególne parametry nie ma sensu :)

·         Perform Runtime Contract Checking – powoduje sprawdzenie poprawności wykonywanych kontraktów podczas uruchomienia aplikacji. Daje to nam możliwość testowania kodu, zamiast akceptacji złego parametru możemy zobaczyć jaki „klient” nie spełnia naszych oczekiwań i zobaczyć dlaczego tak się dzieje, może klient pomija analizę parametrów, które nam przekazuje? Ten combox obok checkbox zawiera trzy pięć wartości:

o   Full – testuje wszystkie typy kontraktów (pre i post oraz inwarianty);

o   Preconditions – testuje jedynie kontrakty pre-conditions czyli Require i RequireAlways;

o   RequiresAlways – testuje tylko i wyłącznie kontrakty RequireAlways.

o   Pre and Post – testuje jedynie kontrakty pre i post condition, ale nie testuje inwariantów;

o   ReleaseRequires – testuje jedynie kontrakty pre-condition Require<TException>;

o   None – przydatny do przeprowadzania testów wydajnościowych, nie testuje niczego.

·         Public Surface Contract Checks – opcja przydatna, kiedy chcemy analizować jedynie wywołania kontraktów w assembly, które są publiczne i mogą zostać wywołane z innych assembly;

·         Assert on Contract Failure – domyślnie opcja jest zaznaczona I chodzi jedynie o to czy ma zostać wygenerowany wyjątek przy np.: Requires czy ma się pojawić okno dialogowe jak przy opisie Ensure. Zaznaczenie – widok jak przy Ensure, odhaczenie wywołanie wyjątku;

·         Call-site Requires Checking – mały bajer zezwalający podczas buildowania projektu A na sprawdzenie kontraktów w bibliotece B, z której projekt A korzysta. Opcja się sprawdza, kiedy biblioteka B nie ma lub ma tylko częściowo włączoną opcję runtime contract checking a do tego zawiera bibliotekę kontraktów B.Contracts, dzięki czemu podczas sprawdzania wywołań metod z projekt A w bibliotece B są sprawdzane warunki pre-condition;

·         Perform Static Contract Checking – umożliwia weryfikację kontraktów w trakcie procesu kompilacji kodu. Daje to nam przyjemne informacje w oknie błędów (Error List), które nakierowują nas na problem jaki może istnieć. Sama analiza jest na tyle ciekawa, że bierze pod uwagę cały kod metody a nie tylko pierwsze linijki. Jeżeli analizator zauważy, że przekazujemy parametr a od którego następnie odejmujemy 10, to poleci nam stworzenie warunku, który powinien być spełniony (screenshot poniżej). Włączenie tej konfiguracji powoduje utworzenie dodatkowego pliku DLL zawierającego jedynie API z kontraktami (bez naszego kodu), umożliwia to przekazanie API na podstawie jakiego klient będzie budował swoje rozwiązanie. Dodatkowo możemy określić następujące wartości analizy statycznej:

o   Implicit Non-Null Obligations – analizuje miejsca, w których może wystąpić wartość null zarówno w pre jak i post;

o   Implicit Array Bounds Obligations – to samo co wyżej ale pod względem zakresu przekroczenia zakresu tablicy;

o   Check in Background – moim zdaniem funkcja jeszcze nie działa, chodzi o to by analiza była wykonywana w trakcie pisania kodu a nie po kompilacji :)

o   Implicit Arithmetic Obligations – nie wiem :(

o   Show squigglesnie wiem :(

o   Baseline – jeżeli opcja jest włączona, należy podać nazwę pliku XML, który jak nie istnieje zostanie utworzony podczas pierwszej kompilacji. Jeżeli chcemy wprowadzić kontrakty do istniejącego już rozwiązania to ilość błędów zwróconych może być przytłaczająca. Baseline umożliwia nam utworzenie listy błędów, która będzie odfiltrowana w trakcie wyświetlania informacji na temat złych kontraktów. Po prostu kompilator będzie wykonywał analizę a następnie pozostawiał tylko te wartości, które nie znajdują sie w pliku baseline.

·         Build a Contract Reference Assembly – jeżeli nie chcemy wykonywać analizy statycznej, a chcemy mieć osobą dll z informacjami na temat kontraktów to należy ten checkbox zaznaczyć :)

Czy to wszystko?

Nie! :) jeszcze jest kilka metod, które pominąłem jednak dwie ciekawsze z nich jeszcze nie mają wsparcia analizatora :( więc zachęcam do przejrzenia punktu zasoby, gdzie są linki do dokumentów/filmów, które wam przybliża elementy które omówiłem, których nie omówiłem i te które nadchodzą:)

Ograniczenia

Pierwszym ograniczeniem jest ważny aspekt w CC – kontrakty są dziedziczone na zasadzie behavioral subtyping, co oznacza iż w metodzie przeciążonej nie możemy definiować pre-conditions, zaś do woli możemy definiować i specjalizować post-conditions. Dodatkowo nie wszystkie jeszcze wyrażenia (które pominąłem – ForAll, Exists) są sprawdzane przez dostarczone narzędzia. Także aktualne narzędzia analizujące nie radzą sobie z kodem wykorzystującym wyrażenie yield – istnieje obejście tego problemu, ale IMO nie warte nawet zwrócenia na razie uwagi, to powinno zostać naprawione :)

Ciągi znaków wykorzystywane w każdej z metod, gdzie mogą być wykorzystane, muszą być statycznie podane – na razie nie ma wsparcia dla dynamicznie bindowanych wartości, ale planowane jest ich wprowadzenie.

Aktualnie, run-time check, działa na zasadzie Debug.Assert(false); a następnie Environment.FailFast("message"); ma to być jednak zmodyfikowane by móc przemienić to na własne wyjątki.

Kontrakty nie działają na strukturach.

Zasoby

Tego jest trochę :)

·         Design By Contract na stronach Wikipedii;

·         Microsoft Research Code Contracts – główna strona CC, gdzie można przeczytać podstawowe zasady i założenia oraz pobrać wersję akademicką rozwiązania;

·         DevLab Code Contracts – strona zawierająca wersję komercyjną rozwiązania, linki do forum i innych zasobów;

·         Code Contract Documentation – dokumentacja w PDF CC – ma 24 strony, warto przeczytać, trochę bardziej szczegółowo opisane to co ja starałem się przekazać :)

·         Code Contracts Forum – tam gdzie możecie zgłaszać bugi oraz zadawać interesujące pytania :)

·         Blog twórców;

·         Research: Contract Checking and Automated Test Generation with Pex – pokrótce omówione CC oraz Pex;

·         Getting started with Code Contracts in Visual Studio 2008 – krótki film instruktażowy na temat CC :)

·         Expert to Expert: Contract Oriented Programming and Spec# - materiał o Spec#, ale dużo z tego jest w CC, nie licząc słów kluczowych :)

Podsumowanie

Jest to nowa ciekawa funkcjonalność dodana do .NET Framework na którą warto zwrócić uwagę. Ja już teraz wykorzystuje CC gdyż znalazłem na nie zastosowanie, nie licząc tego iż analizator statyczny powoduje analizę mojego kodu i wychwycenie błędów które mogłem popełnić, to także wiem, że jak już będę z API korzystał to nie przekażę parametru który nie ma prawa być przekazany.

Oczywiście jak i z TDD, nie należy z kontraktami przesadzać. Jeżeli piszemy kod, który nie jest udostępniony klientowi, to nie ma sensu wykorzystywać kontraktów – przynajmniej z logicznego punktu widzenia. Jeżeli chcemy zaś by cały nasz kod był zgodny z kontraktami, to na początku poświęcimy trochę więcej czasu by potem go odzyskać :)

Jak się podoba pomysł kontraktów? Będziecie z nich korzystać? Zapraszam do dyskusji :)

opublikowano 04 czerwca 09 01:08 przez Gutek | 12 komentarzy   
Zarejestrowano w kategorii: , , , ,
VS 2010 - Call Hierarchy

Kto z was korzysta z .NET Reflector? Pewnie niewielu nie korzysta ;) Więc poniższe okno nie jest wam obce:

A jeżeli jest to zachęcam do jego korzystania do póki nie wyjdzie VS 2010. Za pomocą prawego przycisku myszy na dowolnym elemencie z eksploratora assemblies wybieramy opcję Analyze lub kilkamy Ctrl-R i pokazuje nam się okno umożliwiające prześledzenie hierarchii wywołań poszczególnych elementów.

Możemy zobaczyć gdzie dany element jest wywoływany, od jakich bibliotek jest uzależniony, przez co jest udostępniany i inicjalizowany.

VS 2010 udostępnia nam funkcjonalność zwaną Call Hierarchy. Dzięki niej jesteśmy wstanie dokładnie prześledzić w jaki sposób i gdzie nasz kod jest wywoływany. Funkcja ta jest podobna do Find All References, z tą różnicą iż pozwala ona na hierarchiczne przeglądanie wywołań funkcji wraz z możliwością zagłębienia się w głąb wywołań (a nie tak jak w Find All... lista miejsc gdzie nasz kod jest wywoływany, ale podobnie do tego jak wygląda opcja Analyzer w .NET Reflector).

To co nam udostępnia nowa funkcjonalność to:

1)      Przeglądanie wywołań do elementu;

2)      Przeglądanie wywołań z danego elementu;

3)      Przeglądanie przeciążeń danego elementu;

4)      Widok dokładny w jaki sposób i gdzie następuje opcja X (1-3) dla zaznaczonego elementu.

Do tej pory, maniakalnie korzystałem z Reflectora by przejrzeć nawet wywołania z mojej bibliotece. Nie znoszę tego sposobu który jest udostępniony zarówno przez VS 2008 jak i R#, zaś to co jest w Reflectorze moim zdaniem było i jest intuicyjnie, proste i przejrzyste :) a teraz mam to w lepszej formie w VS 2010 :) Super :)

PS.: Jedynym elementem, którego mi brak w VS w porównaniu do Reflectora to.. Reflectora ;)

opublikowano 01 czerwca 09 12:34 przez Gutek | 1 komentarzy   
Zarejestrowano w kategorii:
VS2010 – Praca na kilku monitorach

To co mnie irytowało w poprzednich wersjach VS to, to iż aby pracować na kodzie w VS na drugim monitorze trzeba było przenosić całe okno środowiska. Zaś praca na kodzie rozciągniętym na dwa ekrany wcale do najprzyjemniejszych nie należała.

Teraz, w VS2010 jest to możliwe! :) mianowicie wszystko w VS jest oknem, które można z dockować lub oddockować. Z dokowane funkcjonalnie przypomina to co mieliśmy w poprzednich wersjach VS, zaś oddockowane umożliwia zarówno przeniesienie okna na drugi monitor! :) jak i go rozciągnięcie pomiędzy monitorami oraz „włączenie full screen” - z przykładu poniżej, to full screen będzie powiększenie okna za pomocą przycisku "maximize", wtedy mamy kod na cały ekran a jedynie pasek tytułu od pliku pozostanie widoczny.

Nie wiem jak wam, ale mnie ta funkcja się podoba :)

opublikowano 28 maja 09 12:36 przez Gutek | 4 komentarzy   
Zarejestrowano w kategorii:
VS2010 – Zaznaczanie tekstu

Jest… super irytujące :) a dokładnie mówiąc IMO jest Bugiem. Normalnie jak macie pole tekstowe, na którym nie ma fokusu, i klikniecie zaznaczanie tekstu od końca to zaczniecie tekst zaznaczać od końca… w VS2010 przy deklaracji projektu jest na odwrót.

Jak klikniecie na pole od końca to zaznacza się tekst od początku. To samo tyczy możliwości zmiany tekstu w połowie pola. Znów wasz kursor powędruje na początek.

zobacznie załączony do postu film (powinien być na dole w stopce postu link do niego).

Pora więc zgłosić pierwszego buga :) zobaczymy jaka będzie reakcja :)

opublikowano 24 maja 09 11:14 przez Gutek | 6 komentarzy   
Zarejestrowano w kategorii: ,
Attachment(s): Win2k8SP1 - VS2010 Movie.zip
REPL

Aplikacje typu REPL (Read, Execute, Print, Loop) są wykorzystywane w celu udostępnienia szybkiego środowiska uruchomieniowego dla kodu pisanego w języku X. Przykładami takich „konsol” jest chociażby konsola F# interactive, czy także konsole IPY i RP udostępnione przez IronPython i IronRuby.

W aplikacjach typu REPL chodzi o to, że input wprowadzony przez użytkownika (READ) jest od razu przetwarzany i wykonywany (EXECUTE) zwracając wynik użytkownikowi (PRINT) dając mu możliwość wprowadzenia następnego polecenia (LOOP). Dzięki czemu programiści nie muszą zaciągać wielkich systemów typu VS w celu sprawdzenia czy ich kod działa, albo co robi funkcja puts z Ruby.

Do tej pory, tworzenie REPL było dość skompilowanym zadaniem, choć na pewno nie, niewykonywanym, przykładem może być chociażby REPL dla języka C# napisany przez Dona Boxa, a następnie rozwinięty przez Ronalda Matta do obsługi wielolinijkowego kodu. Najnowszy REPL dla C# jest dostępny w Mono. Ale jak widać nie są to aplikacje 2-3 linijkowe tylko przeważnie zajmują dość sporo kodu i też nie każda funkcja jest dostępna.

Wraz z nadchodzącym .NET Framework oraz z zintegrowanym z nim DLR (Dynamic Language Runtime, aktualnie dostępnym na CodePlex), nie tylko tworzenie języków na platformę .NET zostanie ułatwione, ale także tworzenie do nich aplikacji typu REPL.

Już od dawna w sieci można znaleźć przykłady jak wykorzystać DLR w celu hostowania języka IronPyton w C# - przykład nawet był podany na CodeCamp Warszawa 2009. Jednakże nie znalazłem jeszcze przykładu, który umożliwiłby rozsądne hostowanie języka ze wsparciem wielolinijkowym. Pod rozsądnym rozumiem to iż nie musimy wciskać specjalnej kombinacji klawiszy w celu wykonania wprowadzonego kodu, ale w zależności od tego co wprowadziliśmy do REPL kod albo się wykona albo poczeka na dalszy INPUT.

Niestety większość przykładów w sieci podaje sposób utworzenia środowiska uruchomieniowego dla języka X za pomocą utworzenia jego silnika (engine) a następnie wykonanie metody Execute na przekazanym ciągu znaków. Oczywiście 3/4 tych przykładów wykorzystuje kod, który już nie istnieje w DLR, i tak naprawdę można o nich zapomnieć ;) Jednak można je sprawdzić do kodu poniżej:

/// <summary>

/// Hosts dynamic engine for specified language.

/// </summary>

public class IronEngine

{

    /// <summary>

    /// Dynamic Language Runtime.

    /// </summary>

    public static readonly ScriptRuntime Runtime = ScriptRuntime.CreateFromConfiguration();

 

    /// <summary>

    /// Gets or sets the language hosting API.

    /// </summary>

    /// <value>The language hosting API.</value>

    public ScriptEngine Engine { get; set; }

 

    /// <summary>

    /// Initializes a new instance of the <see cref="IronEngine"/> class and

    /// creates <see cref="ScriptEngine"/> of specified <paramref name="name"/>.

    /// </summary>

    /// <param name="name">The name of the language hosting API.</param>

    public IronEngine(string name)

    {

        this.Engine = Runtime.GetEngine(name);

    }

 

    /// <summary>

    /// Executes the line of code.

    /// </summary>

    /// <param name="code">The line of code.</param>

    /// <returns>

    /// Execution result.

    /// </returns>

    public object ExecuteLine(string code)

    {

        return this.Engine.Execute(code);

    }

}

Jak widać nie jest to trude, to co najważniejsze to na początku pobieramy konfigurację DLR z App/Web.config za pomocą metody:

ScriptRuntime.CreateFromConfiguration();

Teraz mamy już dostęp do wszystkich dynamicznych języków, jakie zostały ustalone w konfiguracji:

<configuration>

  <configSections>

    <section name="microsoft.scripting" type="Microsoft.Scripting.Hosting.Configuration.Section, Microsoft.Scripting, Version=0.9.6.10, Culture=neutral, PublicKeyToken=null" requirePermission="false" />

  </configSections>

 

  <microsoft.scripting>

    <languages>

      <language names="IronPython;Python;py" extensions=".py" displayName="IronPython 2.6 Alpha" type="IronPython.Runtime.PythonContext, IronPython, Version=2.6.0.10, Culture=neutral, PublicKeyToken=null" />

      <language names="IronRuby;Ruby;rb" extensions=".rb" displayName="IronRuby 0.4" type="IronRuby.Runtime.RubyContext, IronRuby, Version=0.4.0.0, Culture=neutral, PublicKeyToken=null" />

    </languages>

 

    <options>

      <set language="Ruby" option="LibraryPaths" value="..\..\Languages\Ruby\libs\;..\..\..\External.LCA_RESTRICTED\Languages\Ruby\redist-libs\ruby\site_ruby\1.8\;..\..\..\External.LCA_RESTRICTED\Languages\Ruby\redist-libs\ruby\1.8\" />

    </options>

  </microsoft.scripting>

</configuration>

Czyli w przypadku powyżej mamy dostęp do języka IronPython i IronRuby. Wykorzystanie takiego przykładowego REPL jest bardzo proste, wystarczy, że zrobimy coś takiego:

// creating ruby engine

IronEngine ruby = new IronEngine("rb");

// creating python engine

IronEngine python = new IronEngine("py");

 

// testing Add method from created engines

Console.WriteLine("IronRuby Code Add Test:\t\t{0}", ruby.ExecuteLine(string.Format("{0} + {1}", x, y)));

Console.WriteLine("IronPython Code Add Test:\t{0}", python.ExecuteLine(string.Format("{0} + {1}", x, y)));

Proste i przyjemne ;) Można to obrać w while jak i innego rodzaju pętle i mamy jednolinijkowy REPL zaimplementowany. Niestety, nie da to nam możliwości utworzenia klas jak i przetestowania bardziej zaawansowanych funkcji.

Naszym celem będzie napisanie aplikacji konsolowej, tak by można było wykonywać kilka linii kodu z wybranego i z skonfigurowanego w app.config języka – czyli stworzymy sobie multilanguage REPL :)

Zanim przejdziemy do implementacji, proponuje ściągnąć najnowszą wersję DLR z CodePlex (albo źródła, albo dllki). Jest to dość ważne ;) przez DLL od języków dynamicznych jak i od DLR, nie uda nam się wykonać prawie ani jednej linii kodu z przykładu który zaraz napiszemy ;)

No dobrze, środowiska gotowe, więc tworzymy projekt C# Console Application, i dodajemy referencje do następujących bibliotek:

·         IronPython – biblioteka dla języka IronPython;

·         IronPython.Modules – modułu z których IronPython korzysta;

·         IronRuby – biblioteka języka IronRuby;

·         IronRuby.Libraries – zbiór bibliotek z których IronRuby korzysta;

·         Microsoft.Scripting – udostępnia metody do obsługi języków dynamicznych;

·         Microsoft.Scripting.Core – udostępnia kompilator oraz drzewo AST;

·         Microsoft.Scripting.ExtensionAttribute – roszerzenia.

Tworzymy także plik App.config i kopiujemy do niego konfigurację z pobranego DLR (tak będzie łatwiej a plik jest na tyle prosty, że tłumaczyć go chyba nie trzeba).

Mamy już wszystko tak naprawdę przygotowane, jedyne co nam zostało to napisanie kodu :)

/// <summary>

/// Language enumeration.

/// </summary>

public enum Language

{

    /// <summary>

    /// Python.

    /// </summary>

    IronPython,

    /// <summary>

    /// Ruby.

    /// </summary>

    IronRuby

}

 

/// <summary>

/// Read, Execute, Print, Loop Sample.

/// </summary>

public class Repl

{

    /// <summary>

    /// Dynamic Language Runtime.

    /// </summary>

    public static readonly ScriptRuntime Runtime = ScriptRuntime.CreateFromConfiguration();

 

    /// <summary>

    /// Language hosting API.

    /// </summary>

    private ScriptEngine _engine;

    /// <summary>

    /// Scope of the code that is going to be executed.

    /// </summary>

    private ScriptScope _scriptScope;

    /// <summary>

    /// Provides exceptions that occures in code execution.

    /// </summary>

    private ExceptionOperations _exceptionOperations;

    /// <summary>

    /// Gets or sets current language string.

    /// </summary>

    /// <value>The current language string.</value>

    public string LanguageName { get; set; }

    /// <summary>

    /// Gets or sets the current language.

    /// </summary>

    /// <value>The current language.</value>

    public Language CurrentLanguage { get; set; }

 

    /// <summary>

    /// Initializes a new instance of the <see cref="Repl"/> class and

    /// creates IronPython Engine.

    /// </summary>

    public Repl()

    {

        this.LanguageName = "py";

        this._engine = Repl.Runtime.GetEngine(this.LanguageName);

        this._scriptScope = this._engine.CreateScope();

        this._exceptionOperations = this._engine.GetService<ExceptionOperations>();

    }

 

    /// <summary>

    /// Sets the color of the language (the first 3 characters).

    /// </summary>

    private void SetLanguageColor()

    {

        switch (this.CurrentLanguage)

        {

            case Language.IronPython:

                Console.ForegroundColor = ConsoleColor.Yellow;

                break;

            case Language.IronRuby:

            default:

                Console.ForegroundColor = ConsoleColor.White;

                break;

        }

    }

 

    /// <summary>

    /// Prints the language line in selected color.

    /// </summary>

    /// <remarks>

    /// New language line occures ("{0}> ") when the previous code has been executed

    /// or language of the repl has been changed.

    /// </remarks>

    private void PrintLanguageLineNew()

    {

        this.SetLanguageColor();

        Console.Write("{0}> ", this.LanguageName);

        Console.ResetColor();

    }

 

    /// <summary>

    /// Prints the next language line in selected color.

    /// </summary>

    /// <remarks>

    /// Next language line occures ("{0}| ") when the fisrt line is a part

    /// of the complex code (i.e: class definition):

    /// Sample in IronRuby:

    /// <code>

    /// rb> class Calc

    /// rb|     def add(x, y)

    /// rb|         x + y

    /// rb|     end

    /// rb| end

    /// </code>

    /// </remarks>

    private void PrintLanguageLineNext()

    {

        this.SetLanguageColor();

        Console.Write("{0}| ", this.LanguageName);

        Console.ResetColor();

    }

 

    /// <summary>

    /// Execute Repl.

    /// </summary>

    public void Run()

    {

        // while not exit

        while(true)

        {

            // prints language line "py> ", "rb> "

            this.PrintLanguageLineNew();

 

            // read user input

            string line = Console.ReadLine();

 

            // if input is null or empty, replay print line and read

            if(string.IsNullOrEmpty(line))

            {

                continue;

            }

 

            // if line starts with # execute admin command

            if(line[0] == '#')

            {

                // execute admin command

                this.ExecuteCommand(line.Substring(1));

            }

            else

            {

                // execute code

                this.ExecuteCode(this.ReadCode(line));

            }

        }

    }

 

    /// <summary>

    /// Reads the line of code and decide what to do.

    /// </summary>

    /// <param name="line">The line of code.</param>

    /// <returns>

    /// ScriptSource representation of the source code.

    /// </returns>

    public ScriptSource ReadCode(string line)

    {

        // keeps the code that will need to be executed

        StringBuilder code = new StringBuilder();

        // and adds passed line to it.

        code.AppendLine(line);

 

        // while code still needs input from the user

        // continue to ask user for the code.

        while(true)

        {

            // represents a source code and offers few ways to execute it

            ScriptSource scriptSource = this._engine.CreateScriptSourceFromString(code.ToString(),

                                                                                  SourceCodeKind.InteractiveCode);

 

            // if code is completed or is invalid return the source

            if(scriptSource.GetCodeProperties() == ScriptCodeParseResult.Complete

                || scriptSource.GetCodeProperties() == ScriptCodeParseResult.Invalid)

            {

                return scriptSource;

            }

            else

            {

                // otherwise ask for the next line

                this.PrintLanguageLineNext();

 

                line = Console.ReadLine();

 

                // and if this line is empty return the source

                if (string.IsNullOrEmpty(line))

                    return scriptSource;

 

                // or append it and repeat actions

                code.AppendLine(line);

            }

        }

    }

 

    /// <summary>

    /// Executes the the passed source code.

    /// </summary>

    /// <param name="scriptSource">The source code to be executed.</param>

    public void ExecuteCode(ScriptSource scriptSource)

    {

        try

        {

            // execute passed code

            scriptSource.Execute(this._scriptScope);

        }

        catch(Exception ex)

        {

            string message;

            string name;

            // read the exception from the execution

            this._exceptionOperations.GetExceptionMessage(ex, out message, out name);

 

            // set console colors and prints the error.

            Console.BackgroundColor = ConsoleColor.Red;

            Console.ForegroundColor = ConsoleColor.Black;

            Console.WriteLine("{0}: {1}", name, message);

            Console.ResetColor();

        }

    }

 

    /// <summary>

    /// Executes the admin command.

    /// </summary>

    /// <param name="command">The admin command.</param>

    public void ExecuteCommand(string command)

    {

        switch(command)

        {

            case "exit":

                Environment.Exit(0);

                break;

            case "list":

                this.DisplayLanguages();

                break;

            default:

                if (!this.SwitchLanguage(command))

                {

                    Console.ForegroundColor = ConsoleColor.Red;

                    Console.WriteLine("Unknown command '{0}'", command);

                    Console.ResetColor();

                }

                break;

        }

    }

 

    /// <summary>

    /// Switches the language.

    /// </summary>

    /// <param name="name">The language name.</param>

    /// <returns>

    ///     <c>true</c> if swithc successed; otherwise <c>false</c>.

    /// </returns>

    private bool SwitchLanguage(string name)

    {

        ScriptEngine engine;

        if(this._engine.Runtime.TryGetEngine(name, out engine))

        {

            this.LanguageName = name;

            this._engine = engine;

            if (name == "py" || name == "IronPython")

                this.CurrentLanguage = Language.IronPython;

            else

                this.CurrentLanguage = Language.IronRuby;

 

            return true;

        }

 

        return false;

    }

 

    /// <summary>

    /// Displays the languages.

    /// </summary>

    private void DisplayLanguages()

    {

        foreach(var language in Repl.Runtime.Setup.LanguageSetups)

        {

            Console.WriteLine("{0}: {1}", language.DisplayName, string.Join(", ", language.Names.ToArray()));

        }

    }

}

Niby go dużo, ale głównie przez komentarze. To co jest dla nas najważniejsze to zmienne:

/// <summary>

/// Language hosting API.

/// </summary>

private ScriptEngine _engine;

/// <summary>

/// Scope of the code that is going to be executed.

/// </summary>

private ScriptScope _scriptScope;

/// <summary>

/// Provides exceptions that occures in code execution.

/// </summary>

private ExceptionOperations _exceptionOperations;

Które udostępnią nam silnik języka dynamicznego, pozwolą na wykonanie kodu w określonym przez nas zakresie oraz dadzą nam wyniki błędnego wykonania. Także te dwie linijki są bardzo ważne:

// represents a source code and offers few ways to execute it

ScriptSource scriptSource = this._engine.CreateScriptSourceFromString(code.ToString(),

                                                                      SourceCodeKind.InteractiveCode);

 

// if code is completed or is invalid return the source

if(scriptSource.GetCodeProperties() == ScriptCodeParseResult.Complete

    || scriptSource.GetCodeProperties() == ScriptCodeParseResult.Invalid)

{

    return scriptSource;

}

Dzięki nim wiemy kiedy mamy wykonać kod, który wykonuje się bardzo prosto:

// execute code in our scope

scriptSource.Execute(this._scriptScope);

Reszta to jest po prostu „dodatek” by wszystko ładnie wyglądało i dało się z tego korzystać. Także zostały zaimplementowane trzy polecenia administracyjne:

1.       #exit – wychodzi z aplikacji;

2.       #list – wyświetla listę dostępnych języków;

3.       #LANGUAGE – zmienia język na wybrany.

Korzystanie z naszego REPL też jest dość proste:

Repl repl = new Repl();

repl.Run();

W porównaniu do tego, co trzeba było zrobić w C#... to pisanie kodu z wykorzystaniem DLR to czysta przyjemność :)

Powyższy przykład jedynie przedstawia najprostszy wariant REPL, możemy pomyśleć o niebo lepszym napisanym w WPF i udostępniającym kilka języków na raz (za pomocą okien). Możemy także przypatrzeć się implementacji REPL przez IronPython/IronRuby i wykorzystać te same klasy w celu zwiększenia kontrolki nad naszym REPL. Możliwości jest wiele, ja zaprezentowałem jedną z nich :)

Mam nadzieję że znajdę trochę czasu jeszcze by pokazać tak naprawdę co i jak można zrobić za pomocą DLR bo zrobić będzie można naprawdę bardzo dużo :)

opublikowano 24 maja 09 09:51 przez Gutek | 1 komentarzy   
Zarejestrowano w kategorii: , , ,
Trochę z innej beczki

Ale ten czas leci… nim się obejrzałem a zaleciał cały miesiąc, a w nim trochę się wydarzyło :)

Po pierwsze Speaker Idol Warszawa dla CodeCamp Warszawa 2009. Chyba nikt tego nigdzie nie napisał ani nie ogłosił, więc zwycięzcą Warszawskiego SI jest Andrzej Piotrowski – gratulacje i do zobaczenia już w tą sobotę :)

Po drugie, konkurs PGS okazał się wielkim nie wypałem :( dosłownie nikt się nie zgłosił :( ani nikt nic nie napisał, nie zadał pytania nic :( szkoda, bo licencje właśnie się przedawniają. Jednakże nauczyliśmy się coś z tego! I następny konkurs będzie już lepiej zorganizowany – mam nadzieję, że także i nagrody będą odpowiednio ciekawe :)

Po trzecie :) już wcześniej wspomniana konferencja. Tutaj trzeba podziękować grupie WG.NET jak i Łódzkiej Grupie Profesjonalistów IT & .NET za zajęcie się organizacją. Grupa PGS pomagała, ale nie robiła aż tyle ile Łódzka i WG.NET. Dzięki chłopaki i mam nadzieję, że konferencja CodeCamp Warszawa 2009 wyjdzie tak dobrze jak dobrze zapowiadają się prelegenci :)

Po czwarte, znajomi wypuścili wersję beta aplikacji WebEFS:

System webEFS został stworzony z myślą o instytucjach i osobach zaangażowanych w realizację projektów finansowanych z Europejskiego Funduszu Społecznego, a w szczególności projektów finansowanych z Programu Operacyjnego Kapitał Ludzki.

Zachęcam do korzystania z niej, naprawdę jest to bardzo dobry pomysł jak i super wykonanie :)

Po piąte, grupa PGS organizuje dwa spotkania, jedno merytoryczne, a drugie filozoficzne ;)

Na merytorycznym, będziemy mówić o dwóch sposobach prezentacji elementów listy: XSLT DataView Web Part oraz SPGridView. Różnica pomiędzy dwoma rozwiązaniami polega w sposobie ich tworzenia, jedno można zrobić za pomocą SharePoint Designera, drugie za pomocą Visual Studio. Spotkanie będzie o tyle ciekawe, że zaprezentujemy sposoby tworzenia pewnych funkcjonalności i postaramy się odpowiedzieć na zasadnicze pytanie: kiedy używać XSLT DataView Web Part a kiedy SPGridView. Sesję na temat XSLT DataView Web Part poprowadzi Paweł Wróbel, kierownik projektów z firmy Datapolis – twórcy takiego rozwiązania jak Workbox. Zaś o SPGridView opowiem Ja :D

Wszystkich zainteresowanych zapraszamy na stronę informacyjną spotkania, stronę spotkania i do rejestracji.

Na spotkaniu filozoficznym będziemy się dzielić spostrzeżeniami dlaczego SharePoint jest taki a nie inny, i dlaczego ptaszki ćwierkają a słońce świeci :) Czyli tak zwane ShareBeer! :) Zapraszamy na stronę informacyjną spotkania, stronę spotkania i do rejestracji (uwaga, trzeba być członkiem PGS by móc się zarejestrować na piwo).

Po szóste, WYSZEDŁ Visual Studio 2010 Beta 1! :) A to mi spędza aktualnie sen z oczu bo po 12h pracy wracam, i włączam swoją wirtualkę ;) więc możecie się spodziewać niebawem kilku ciekawych postów :) Zresztą % już rozpoczął ;)

Tym o tak prostym postem, podsumowuje to co się działo, dziać będzie oraz to co zamierzam w najbliższym czasie publikować ;) A tym czasem idę dokończyć swoją kawę i wracam do lektury Punktu Przełomowego – polecam :)

opublikowano 22 maja 09 01:42 przez Gutek | 4 komentarzy   
Zarejestrowano w kategorii:
Framework Design Studio

Projektowanie dobrego API nigdy nie było łatwe, o czym świadczy chociażby implementacja IOleCommandTarget::Exec, czy też niedawno wypuszczony SharePoint 2007. Co z tego, że nasz produkt pozwala nam zbudować roller coster (rysunek 8), kiedy dla innych udostępniamy jedynie huśtawkę zamiast pnia (rysunek 3) – link, zapożyczyłem rysunek z procesu tworzenia oprogramowania, ale mniej więcej tak wygląda API, które w większości jest udostępnione programistom.

Problem tworzenia dobrego API istnieje od zawsze i z będzie istniał zawsze, jedynie co my możemy zrobić to ułatwić sobie proces tworzenia API tak by móc się nawzajem informować co należałoby poprawić i dlaczego. Aktualnie mamy diagram klas, opis diagramu, .NET Reflector oraz Object Browser w VS i samo VS. To nam umożliwi przejrzenie API i jak będziemy mieli jakieś uwagi, to zapiszemy to w osobnym dokumencie i prześlemy do wszystkich zainteresowanych.

No tak, ale kiedy mamy 100 klas, z czego ponad połowa udostępniona jest jako API zewnętrzne i teraz mielibyśmy przeglądać diagram tych klas, i na podstawie diagramu tworzyć opisy to można by dostać kociokwiku od samej ilości klas a co dopiero kiedy taka jedna klasa ma 20 metod i 15 własności? I w której przestrzeni nazw się ona znajduje? Jakby do tego jeszcze użyć Object Browser to już w ogóle może szlak człowieka trafić. No dobrze, mamy .NET Reflectora, który umożliwi nam dość łatwe przejrzenie kodu, ale co z komentarzami? Możemy wykorzystać już dostępny plug-in, jednak .NET Reflector udostępnia znacznie za dużo, to znaczy zamiast koncentrować się czysto na API możemy zbędnie zagłębiać się w implementacje, ja to widzę tak, jakbyśmy chcieli zapisać numer telefonu do znajomego za pomocą napisania aplikacji, która za pomocą e-mail prześle nam telefon na naszą skrzynkę pocztową. Do prostego i ograniczonego zadania zaciągamy parowóz.

Jeszcze innym rozwiązaniem jest wykorzystanie VS do przeglądania kodu i udostępnionego API, jednak znów – parowóz, poza tym, IMO w .NET Reflector łatwiej jest się poruszać po typach i metodach niż w VS, nawet z wykorzystaniem R#.

Mamy jeszcze jedną opcję, wykorzystać Framework Design Studio stworzone przez Krzysztofa Cwaline, Hongping Lim, Davida Fowlera oraz Nicka Moloneya. Studio to, umożliwa cztery podstawowe funkcje:

1.       Przeglądanie API;

2.       Komentowanie API;

3.       Porównywanie dwóch wersji API;

4.       Eksport do MS Word.

Przeglądanie API jest podobne do tego co mamy w .NET Reflector, z tą różnicą, iż nie zagłębiamy się w implementacje poszczególnych metod.

Porównywanie dwóch wersji API umożliwia podgląd tego co zostało usunięte z jednego API, to co zostało nie zmienione oraz to co zostało dodane – na czerwono to co zostało usunięte, na zielono to co zostało dodane, na czarno bez zmian zaś na szaro to co zostało odziedziczone.

Komentowanie, jest tak samo proste jak w Word, zaznaczamy fragment kodu, który chcemy z komentować, klikamy prawym przyciskiem myszy i zaznaczamy Add Comment.

Zaś eksport do Word… fajnie że jest, ale osobiście jak widzę kod źródłowym w dokumencie i to jeszcze ułożonym poziomo (landscape), to przypomina mi się książka na która swojego czasu natrafiłem w Anglii: Kod źródłowy kernela. Po prostu ręce mi opadły, ja wiem, że kiedyś tak się „analizowało” kod i czasami nawet dalej robi… ale wydawać książkę, która zaczyna się od linijki kodu, w środku jest kod i na końcu też jest kod to już przesada. Nie licząc, że zawijanie linii było dość fatalnie zorganizowane przez co czytało się to jeszcze gorzej :/ ale co zrobić kiedy się czeka na premierowe wydanie 2 tomu Harrego Pottera bez którego do domu lepiej nie wracać bo siostra oczy wydłubie ;) Jednak niektórzy mogą uznać to za fajny feature ;)

Nie twierdzę, że FDS załatwi wszystko, ale jest to przyjemne narządko, dzięki któremu możemy szybko i sprawnie przeprowadzić recenzje naszego API przy czym nie będziemy kuszeni zrobieniem czegoś więcej niż czystej recenzji.

Czy wam przypadnie do gustu jak i mi, to zależy :) jednak mam nadzieję, że chociaż na to rzucicie okiem i sami zadecydujecie czy jest to fajnie :) czy jest to kolejny beznadziejny produkt nikomu do szczęścia nie potrzebny :)

opublikowano 04 maja 09 11:45 przez Gutek | 5 komentarzy   
Zarejestrowano w kategorii: ,
Playing For Change

Uwaga, z nieznanych mi bliżej przyczyn filmy nie pokazują się na IE 8, na FF i Chrome działają poprawnie :(

Dwa dni temu otrzymałem od znajomego maila do piosenki Stand By Me, która została nagrana przez nieznanych ulicznych artystów poprzez inicjatywę Playing For Change. Co jest ciekawe w tym wszystkim, to że utwór powstawał w kilku etapach, pierwszym było nagranie podstawowego tracku: dzwięk i głos, przez Roger Ridley z Santa Monica, California. Ta nagrana podstawa została zabrana do nowego orleanu, Louisiana, gdzie niewidomy uliczny muzyk Grandpa Elliott dodał drugi wokal oraz harmonijkę słuchając na słuchawkach podstawy nagranej przez Rogera. W tym samym mieście Washboard Chaz’s dodał „metalową” perkusję na tarce.

Następnie tak przygotowana ścieżka dźwiękowa została zabrana na podróż dookoła świata, od Europy, przez Afrykę do Południowej Ameryki, dodając kolejne wokale i instrumenty.

Wersja finalna teledysku wygląda tak (polecam obejrzenie od początku do końca, warto także zajrzeć na stronę by zobaczyć pełną listę artystów biorących udział w konkretnym nagraniu) – IMO jedna z najlepszych jakie słyszałem/widziałem tej piosenki:

Zaś o całym projekcie w trakcie którego powstała ta piosenka można posłuchać tutaj:

Osobiście lubię takie inicjatywy, i podoba mi się to, że ludzie potrafią jeszcze tworzyć muzykę a przy tym czerpać z tego przyjemność.

Jeżeli jesteście zainteresowani kolejnymi teledyskami inicjatywy, to zapraszam na stronę Epizodów.

opublikowano 01 maja 09 12:54 przez Gutek | 12 komentarzy   
Zarejestrowano w kategorii: ,
.NET i przeszukiwanie Active Directory

Ostatnio bawię się odpytywaniem Active Directory o użytkowników, grupy i jednostki organizacyjne i natrafiłem na kilka dość ciekawych przypadków kiedy to znaki specjalne nie są zamieniane, albo zamienione nie działają poprawnie :) co jest trochę… dziwne ale tak bywa :)

Ogólny problem ze znakami specjalnymi w AD jest taki, iż w niektórych przypadkach można zastosować backslash \ w celu określenia, że kolejny znak po nim ma być traktowany tak jak został zapisany a nie jako znak specjalny, a w niektórych nie. Czyli na przykład nazwa grupy USERS/POWER USERS powinna zostać zamieniona na USERS\/POWER USERS ale w zależności od kontekstu znak / musi zostać zamieniony na inny.

1)      Mianowicie, kiedy my odpytujemy się AD o dany obiekt zawierający znak specjalny to my musimy zadbać o konwersję znaków na poprawne.

2)      Kiedy pobieramy wynik zapytania w postaci ścieżki (SearchResult.Path), .NET Framework zadba o konwersję znaków specjalnych na poprawne escape characters oraz dodatkowo doda nam protokół LDAP:// do początku Distinguished Name od obiektu, którego znaleźliśmy.

3)      Kiedy pobieramy własność Distinguished Name obiektu z DirecotryEntry to dostaniemy ciąg znaków bez zamienionych znaków specjalnych oraz bez protokołu LDAP://.

Różnica pomiędzy 2 i 3 jest taka, że SearchResult.Path zwraca własność ADsPath zaś Distinguished Name jest trochę inną wartością, jednakże za pomocą obydwóch wartości możemy obiekt DirectoryEntry, który zwróci nam odpowiedni obiekt z AD.

Żeby było zabawniej kiedy wyszukujemy musimy wykonać inną podmianę niż w przypadku 2 i 3 :)

Dla elementów, które wyszukujemy musimy podmienić wszystkie zwykłe nawiasy (), Slash, gwiazdkę, backslash, i tak zwany character return/NULL (\0), zaś w Distinguished Name musi zapewnić by przecinki były dobrze odbierane jak i slash – znaków jest więcej a można do nich zaliczyć <>, „”, przecinek, +, =, ; i spację.

Sprawę pierwszą z wyszukiwaniem, możemy rozwiązać za pomocą Extension Method:

/// <summary>

/// Replaces special characters in search string.

/// </summary>

/// <param name="source">The search string.</param>

/// <param name="escapeWildcards">if set to <c>true</c> escape wildcards.</param>

/// <returns>String with proper characters.</returns>

/// <remarks>

/// All characters are replaced, please see the full list:

/// http://msdn.microsoft.com/en-us/library/aa746475%28VS.85%29.aspx

/// </remarks>

public static string ReplaceSpecialCharsInSearch(this string source, bool escapeWildcards)

{

    source = source.Replace("\\", "\\5c").Replace("/", "\\2f");;

    source = source.Replace("(", "\\28").Replace(")", "\\29");

    source = source.Replace("\0", "\\00");

 

    if(escapeWildcards)

        source = source.Replace("*", "\\2a");

 

    return source;

}

Z drugą jest już gorzej ale i lepiej :) Mimo pełnej listy zakazanych znaków, tylko jeden jest domyślnie nie konwertowany, slash, więc kolejna Extension Method wygląda następująco:

/// <summary>

/// Replaces special chars in distinguished name.

/// </summary>

/// <param name="source">The distinguished name.</param>

/// <returns>String with proper characters.</returns>

/// <remarks>

/// Only / is replaced as others should be done by .NET Framework:

/// http://msdn.microsoft.com/en-us/library/aa366101%28VS.85%29.aspx

/// http://msdn.microsoft.com/en-us/library/aa772316%28VS.85%29.aspx

/// http://msdn.microsoft.com/en-us/library/aa746384%28VS.85%29.aspx

/// </remarks>

public static string ReplaceSpecialCharsInDistinguishedName(this string source)

{

    string formatString;

 

    if(source.Contains(@"\/"))

    {

        return source;

    }

 

    int indexOf;

 

    if((indexOf = source.IndexOf("://")) > -1)

    {

        var protocol = source.Substring(0, indexOf + 3);

        source = source.Remove(0, protocol.Length).Replace("/", @"\/");

        formatString = string.Format("{0}{1}", protocol, source);

    }

    else

    {

        source = source.Replace("/", @"\/");

        formatString = "{0}";

    }

 

    return string.Format(formatString, source);

}

Używam wykrycia protokołu ze względu na to by już nie myśleć czy aby na pewno ktoś nie połączył już distinguished name z protokołem, dodatkowo, nie koniecznie musi być LDAP, może być GC (Global Catalog).

Metody te zaoszczędziły mi trochę czasu i okazują się bardzo przydatne, wyszukiwanie podmieniam podczas deklaracji filtru w DirectorySearcher, na przykład:

using(DirectorySearcher deSearch = new DirectorySearcher())

{

    deSearch.SearchRoot = AdUtils.GetDirectoryObject();

    deSearch.Filter = string.Format("(&(objectClass=user)(objectCategory=person)(sAMAccountName={0}))", userName.ReplaceSpecialCharsInSearch());

    deSearch.SearchScope = SearchScope.Subtree;

    SearchResult results = deSearch.FindOne();

 

    if (results == null)

    {

        return null;

    }

 

    return AdUtils.GetDirectoryObject(results.Path);

}

Zaś metodę rozszerzającą string dla distinguished name, używam podczas tworzenia DirectoryEntry:

/// <summary>

/// Gets the <see cref="DirectoryEntry"/> object.

/// </summary>

/// <param name="domainReference">The domain reference (AD Path).</param>

/// <param name="userName">Name of the user.</param>

/// <param name="password">The password.</param>

/// <returns>

/// New <see cref="DirectoryEntry"/> object.

/// </returns>

internal static DirectoryEntry GetDirectoryObject(string domainReference, string userName, string password)

{

    if(string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password))

        return new DirectoryEntry(domainReference.ReplaceSpecialCharsInDistinguishedName());

    else

        return new DirectoryEntry(domainReference.ReplaceSpecialCharsInDistinguishedName(), userName, password, AuthenticationTypes.Secure);

}

Tutaj warto odwołać się jeszcze do przykładu z wyszukiwaniem. Mianowicie, większość obiektów, z których korzystamy podczas pracy z AD implementuje IDisposable poprzez dziedziczenie z klasy komponentu, czyli zarówno obiekty DirectorySearcher jak i DirectoryEntry, powinny zostać zniszczone pod koniec pracy z nimi. Normalnie można tego dokonać wywołując metodę Close na DirectoryEntry oraz Dispose na DirectorySearcher. To powoduje kolejną rzecz, o której musimy pamiętać, dlatego też warto stworzyć sobie extension method do DirectorySearcher, które podczas wywołania metody FindAll zamiast zwracać kolekcję SearchResultCollection, która powinna zostać zniszczona po skończeniu jej użytkowania zwraca IEnumerable<SearchResult>.

/// <summary>

/// Executing a safe find all method, which helps with disposable collection.

/// </summary>

/// <param name="searcher">The searcher.</param>

/// <returns>

/// List of search results.

/// </returns>

internal static IEnumerable<SearchResult> DisposeSafeFindAll(this DirectorySearcher searcher)

{

    using(SearchResultCollection results = searcher.FindAll())

    {

        foreach(SearchResult result in results)

        {

            yield return result;

        }

    }

}

To chyba na tyle z takich ciekawostek i pomocnych funkcji/metod przy zabawie z AD. Mimo wszystkich postów na temat LDAP, które wychwalają protokół jak nie wiem co, wciąż jednak możemy natrafić na problemy w trakcie zabawy. Pytanie teraz tylko pozostaje, czy to jest poprawne działanie – to znaczy czy jest to działanie zamierzone? – czy jest to po prostu Bug, który istnieje i nie został do tej pory naprawiony.

Znacie jeszcze jakieś podobne problemy z odpytywaniem AD? Zachęcam do komentowania, jeżeli o czymś zapomniałem to napiszcie, postaram się poprawić/dodać przykłady.

opublikowano 30 kwietnia 09 11:55 przez Gutek | 2 komentarzy   
Zarejestrowano w kategorii: ,
Był sobie Steve - Aktualizacja

Na końcu postu dodałem linki do Video i Fotorelacji.

No właśnie, był i już go chyba nie ma. Nam społeczności została dana szansa spotkania się z nim 23 kwietnia o czym dużo osób pisała w tym i ja ;)

Ociągałem się z opisaniem spotkania bo chciałem zobaczyć relację innych w Internecie – bo sam nie wiem czy coś ze mną jest źle czy po prostu ze wszystkimi innymi ;) wolałem nie ryzykować – na szczęście ani ze mną ani z innymi nie jest źle :D

Spotkanie samo w sobie trwało krótko, ale prawie zgodnie z planem, różnie osoby podają od 40-50 minut, dla mnie trwało 45 ;) i niech tak zostanie.

Z miejsca zaznaczę, że spodziewałem się czegoś kompletnie innego, niż zastałem, więc to może wpływać na moją ocenę.

Na samym początku uraziła mnie dość jedna rzecz, muzyka. Wśród naprawdę wielu dobrych wykonawców w Polsce zostali wybrani Ci, których można posłuchać w Eska czy RadioZet i chyba wyłącznie tam. Do tej pory jak widziałem Steva to naprawdę muzyka była dobierana bardzo dobrze, trafiały się perełki mniej lub bardziej znane polskiej społeczności. Tym razem jednak miałem notoryczne dudnienie w uszach ;) wiem, wiem nie należy zbytnio do ludzi akceptujących wszystkie rodzaje muzyki. Jeżeli ma lecieć RadioZet, to lepiej by puścili teleaudio – przynajmniej można się pośmiać. Oczywiście nie twierdzę, że RadioZet i Eska puszczają tylko „music for masses” czasami pewnie się trafią rarytasy, choć ja przeważnie mam do nich pecha.

No dobrze, tyle narzekania na muzykę, w końcu nie po to tam przyjechałem :)

Steve na salę wszedł w szaliku kibiców polskich, i z uśmiechem na twarzy zademonstrował go jak znany „wysoki” polityk w państwie – do góry nogami, jednakże jemu udało się to dostrzec i szybko naprawił swój błąd a następnie szalik odrzucił, zawahał się i powiedział, że przeprasza niektórzy mogą to potraktować jako obelgę, ale co tam ;) (tak ja to pamiętam, jak będzie video to będzie można to zweryfikować). Wyszedł on w krawacie, i nie można mu było robić zdjęć ani wnosić plecaków na salę – co mnie trochę dziwi, choć pewnie akcja rzucaniem jajka w Steva dała MSowi do myślenia. Jednakże przy 100 osobach takie środki bezpieczeństwa, kiedy na MVP Summit około 300 osób notorycznie pstrykało mu zdjęcia, zaś osób z plecakiem była połowa jak nie więcej (z 1500 MVP). W nawiązaniu do krawata, no właśnie był on w krawacie, co dało mi trochę do myślenia i wpłynęło pozytywnie na ocenę całości. Jednakże o tym za chwilę :)

I tu nastąpiło coś, czego się nie spodziewałem/oczekiwałem, mianowicie nie mówił on o tym co MS planuje wdrożyć, jakie korzyści te rzeczy dadzą nam i jakie ma plany i jak bardzo lubi społeczność a jedynie wspomniał o kryzysie (o tym później) oraz trochę o nowych rzeczach – Windows, VS, Office pewnie w tym roku, SharePoint w przyszłym. Trwało to… 10 minut, dwa slajdy i basta ;) co prawda do uzupełnienia jego treści nie potrzeba było więcej slajdów :) ale jednak… trochę dziwnie.

No dobrze, zanim jednak przejdziemy do tego co trwało 35 minut, połączę dwa wątki: krawat i kryzys. Tak jak Steve wspomniał trwa kryzys, który spowoduje mniejszą ilość sprzedanych komputerów, telefonów komórkowych, serwerów itp. itd. Ogólnie firmy zajmujące się integracją, będą miały w tym roku kiepsko. Jednakże w dobie kryzysu jak zauważył Steve wciąż jest miejsce dla innowacyjności i to będzie się sprzedawać. Teraz tutaj nie zgodzę się z rozumowaniem Procenta w tej kwestii, nie chodzi tutaj o super extra rozwiązania jak wielodotykowy okren itp., a chodzi o to, iż teraz osoba mająca pomysł na stworzenie rozwiązania, które spowoduje obniżenie kosztów działania przedsiębiorstwa w zauważalnym stopniu w ciągu kilku miesięcy będzie miał dużo do roboty. Firmy szukają możliwość cięć kosztów, a jednym z możliwych sposobów jest informatyzacja/automatyzacja pewnych aspektów działania firmy. Stąd teraz siła małych dynamicznych firm w porównaniu do wielkich kolosów, które szukają dużych klientów, bo chcą utrzymać markę firmy „działającej dla znanych dużych korporacji”. Mała firma może sobie pozwolić na szukanie i tworzenie pomniejszych ciekawych i pomocnych rozwiązań, duże firmy mają swoje stawki, nawet wewnętrznie, za które ktoś musi zapłacić. A jak nie ma kasy to też nie ma kasy na to by swobodnie pisać kod, który nigdy się nie sprzeda. Więc jeżeli macie pomysł jak usprawnić działanie firmy, stwórzcie soft i go sprzedajcie! Gwarantuje, że teraz jest na to czas i miejsce. No dobrze, a gdzie krawat? Właśnie tutaj. Wizyta Steve w Polsce na pewnie nie była bezcelowa. W przeciągu prawie chyba już 15 lat jak nie więcej działania MS w Polsce Bill był 2 razy i 2 razy był Steve, za każdym razem po ich wizycie coś się działo. I tym razem IMO też się stało – finał Imagine Cup będzie w Warszawie, podpisana została umowa pomiędzy polską policją a Microsoft na system CETS. Do tego Steve potem miał masę spotkań z politykami a jak wiadomo na nich trzeba wyglądać ;) Mogę też się założyć, że do łatwych one nie należały. Stąd moje tłumaczenie dlaczego tak fajnie na spotkaniu nie było jak mogłoby być :)

Po tych krótkich 10 minutach i mojego rozpisania się na pół strony A4, nastąpiła sesja Q&A, do której pytania zgłaszali uczestnicy. IMO pytań ważnych i dobrych nie było, raczej pytania lekkie, na które można dość szybko odpowiedzieć. Nie będę się o nich rozpisywał, warto jednak zaznaczyć, że mimo pilnowania tego by pytania było zadawane przy mikrofonie, udało się jednej osobie z publiczności zadać pytanie czemu wciąż nie ma XBox Live PL. Ciekawa też była reakcja Steva, który na chwilę z pochmurniał a następnie odpowiedział dyplomatycznie, że on o tym nic nie wie i wyją swoje żółte karteczki i zanotował sobie to pytanie by później się temu przyjrzeć :) To jeszcze jest warto zanotowania, to pochwalenie Apple za to co robi, i jak to robi – choć to już nie pierwszy raz, na MIX’09 było nawet pokazanie jak sprzedaż ładnej rzeczy można jeszcze bardziej zwiększyć i z popularyzować na podstawie iPodów i ich kolorków. Jednakże jak zauważył Steve, Apple zajmuje jedynie 3% rynku komputerów na świecie, są ładne, ale i drogie, są nakierowane na określoną grupę, kiedy MS chce dotrzeć do największej liczby osób. Ostatnio nawet jakość noebooków Appla spadła z pierwszego miejsca na trzecia (na pierwszym o dziwo jest Asus/Acer jak dobrze pamiętam). Ale miło słyszeć takie słowo od strony MS. Może w końcu wojna Applowiec-Windowsowiec się zakończy… choć wątpie :(

O ciekawych pozostałych pytaniach można przeczytać u Binka i Procenta na blogu więc nie będę tutaj ich powatrzał :)

Moja ogólna ocena spotkania? Jak na spotkanie, na które zostali zaproszeni ludzie z całej polski nie robiło wrażenia, oraz nie dawało takiej satysfakcji jakby mogło dać. Zaś samo zobaczenie Steva i przyjrzeniu się jego sposobowi prowadzenia prezentacji – bezcenne :) Podziwiam go za jego charyzmę i spontaniczność, za to, że potrafi porwać tłumy kiedy trzeba a kiedy nie, być spokojnym i opanowanym a przy tym momentami rzuć naprawdę dobry dowcip kontekstowy. Materiały ze spotkania powinne być udostępnione więc sami będziecie mogli ocenić która relacja odpowiada waszym odczuciom :)

Ja tym czasem idę po kolejną dużą americano w Karma Cafe a potem pewnie do Planu B na piwko :) Do Starbucks nie udało mi się dotrzeć – kolejka jak za PRLu. Ale dla tych wszystkich, którzy twierdzą, że jest tam drogo – nie prawda jest tak samo drogo jak w Cafe Haven czy w innych kawiarniach w Warszawie więc nie rozumiem czemu w gazetach i w TV mówią o najdroższej kawie w mieście :)

Video jest dostępne pod tym adresem, zaś fotorelacja pod tym. Dodatkowe linki i relacje zostały zgrupowane w artykule o Stevie na WSS.pl.

opublikowano 26 kwietnia 09 07:45 przez Gutek | 0 komentarzy   
Zarejestrowano w kategorii:
Więcej wypowiedzi Następna strona »