sobota, 31 maja 2008

PostSharp 1.0 RC3 wydany

Postsharp to bardzo ciekawy framework (biblioteka, mechanizm?) pozwalający zrealizować programowanie aspektowe (AOP) w .NET. Jako, że jeden przykład wart jest tysiąca słów - oto przykładowy aspekt pozwalający wywołać metodę w transakcji (opakować wywołanie metody za pomocą TransactionScope):

[Serializable]
public class TransactionScopeAttribute: OnMethodInvocationAspect
{
public override void OnInvocation(MethodInvocationEventArgs eventArgs)
{
using (var ts = new TransactionScope())
{
eventArgs.Proceed();
ts.Complete();
}
}
}


Oraz jego zastosowanie w praktyce:


class Program
{
static void Main(string[] args)
{
MyDbMethod();
}

[TransactionScope]
private static void MyDbMethod()
{
//...
}
}



Jego największą przewagą nad innymi rozwiązaniami (Policy Injection Application Block, AOP w Spring.net, itp.) jest fakt, że pozwala przechwycić praktycznie każdą metodę - nie wymaga, aby klasa implementowała interfejs, czy też dziedziczyła po MarshalByRefObject. Co więcej, umożliwia przechwytywanie wywołań konstruktorów, metod statycznych, prywatnych, itp. Tak szerokie możliwości wynikają z odmiennego mechanizmu działania. PostSharp po wygenerowaniu assembly modyfikuje kod IL wszystkich przechwytywanych metod, tak aby umożliwić przechwycenie wywołania.



Warto zaznaczyć, że framework jest darmowy i ma otwarty kod źródłowy (na licencji GPL/LGPL v3).



Wiecej informacji na stronie projektu.



PS. Zgodnie z informacją na blogu, PostSharp będzie prezentowany na CodeCamp 7 czerwca w Krakowie. Uważam, że tej prezentacji nie można przegapić!

poniedziałek, 19 maja 2008

Finalny Enterprise Library 4.0

Już od 4 dni dostępne jest finalne wydanie Enterprise Library 4.0. Bibliotekę można pobrać ze strony (wymaga rejestracji), dokumentacja dostępna jest na MSDN.

Nowości względem wersji 3.1:

  • Nowy blok - Unity Application Block. Jest to lekka i rozszerzalna implementacja kontenera Dependency Injection.
  • Wszystkie assembly są oznaczone jako Allow Partially-Trusted Callers (APTCA), dzięki czemu Enterprise Library może być wykorzystywany przez kod "częściowo zaufany". Jest to bardzo przydatne w środowiskach hostowanych - aktualnie można bezpośrednio wykorzystać dostarczone podpisane assembly bez konieczności własnej rekompilacji.
  • W Policy Injection Application Blok wszystkie CallHandlers mają nowy atrybut Order (typu int), który określa kolejność, w jakiej mają być nakładane polisy. Jest to bardzo przydatna zmiana, gdyż dzięki niej można na przykład zarządać, aby autoryzacja dostępu do metody odbyła się przed walidacją jej parametrów.
  • Przy używaliu ValidationCallHandler mamy możliwość określenia nazw zestawów reguł (RuleSet), jakie mają zostać użyte do walidacji parametrów.
  • Wsparcie dla WMI 2.0 i poprawiona instrumentacja.
  • Możliwość wpięcia własnego CacheManagera (poprzez interfejs ICacheManager)
  • Zmiany w Logging Application Block.
  • Inne (poprawki błędów, poprawki wydajności, nowe liczniki wydajności)
  • Wsparcie dla .NET 3.5 i VS 2008.

Ostatni punkt jest właściwie zaletą i wadą jednocześnie, gdyż wymagany jest .NET 3.5. Dla projektów tworzonych dla .NET 2.0 i 3.0 pozostaje EntLib 3.1. Pocieszeniem jest fakt, że Unity można pobrać oddzielnie i wymaga on już tylko .NET 2.0 lub nowszego.

W najbliższym czasie mam zamiar przyjrzeć się Unity oraz opisać moje dotychczasowe doświadczenia z Policy Injection (który moim skromnym zdaniem nadał nowy sens całemu Enterprise Library).

czwartek, 15 maja 2008

LINQPad

Właśnie dodałem sekcję "Moje aktualne top 10", w której będę umieszczał programy, strony i wszystko inne, co aktualnie wydaje mi się najbardziej ciekawe. LINQPad jest jej pierwszą (i na razie jedyną) pozycją. Napisany został przez Josepha Albarhari, autora książki "C# 3.0 in a Nutshell".

Program ten to tzw. "must have" każdego, kto ma zamiar używać technologii LINQ (to SQL, to Objects, to XML). W skrócie jest to mały edytor pozwalający odpalać wyrażenia C#/VB oraz kawałki kodu. Dzięki niemu można przetestować każde wyrażenie LINQ. Wszystko mówiący screen:

image

Cudo to potrafi:

  • łączyć się z bazami MS SQL,
  • wyświetlać wyniki zapytań,
  • wyświetlać kod SQL zapytania,
  • wyświetlać zapytanie LINQ skonwertowane do extension methods oraz wyrażeń lambda;
  • zawiera 200 przykładów zapytań z książki "C# 3.0 in a Nutshell" - świetny materiał do nauki LINQ.

Aplikacja jest dostępna na stronie http://www.linqpad.net/ i co najważniejsze jest darmowa.

sobota, 10 maja 2008

foreach - zagadka

Proponuję prosty acz kształcący konkurs ;). Pytanie jest proste - jakie są trzy sposoby na zdefiniowanie klasy Foo, aby poniższy kod się skompilował:

Foo f = new Foo();
foreach (object o in f)
{

}

Osoba, która poda wszystkie 3 w nagrodę bedzie mogła pozdrowić znajmonych na łamach mojego bloga ;).

Powierzchowna refleksja na atrybuty, czyli CustomAttributeData w akcji

Fakty o klasie CustomAttributeData

Pełne informacje: oczywiście MSDN

A tak konkretnie: CustomAttributeData to klasa przechowująca informacje o atrybucie opisującym typ, członka, parametr,... W odróżnieniu od metody GetCustomAttributes (z Type, MemberInfo, ParameterInfo, itd.) nie tworzy ona instancji atrybutów, ale daje jedynie informacje o tym:

  • jakie parametry zostały przekazane do jego konstruktora,
  • jakie nazwane parametry (w postaci Nazwa=Wartość) zostały zadeklarowane,
  • który konstruktor został wywołany.

Innymi słowy - CustomAttributeData zawiera przepis, jak utworzyć instancję danego atrybutu, a np. Type.GetCustomAttributes() zwraca już utworzone atrybuty.

Zgodnie z zasadą, że jeden przykład zastępuje tysiąc słów - spróbujmy dowiedzieć się jakie atrybuty ma typ System.Web.UI.Page:

[Designer("Microsoft.VisualStudio.Web....", typeof(IRootDesigner))]
[DefaultEvent("Load")]
[DesignerSerializer("Microsoft.VisualStudio...", "System.ComponentModel...")]
[DesignerCategory("ASPXCodeBehind")]
[ToolboxItem(false)]
public class Page //...



 


W tym celu wywołujemy statyczną metodę GetCustomAttributes klasy CustomAttributeData:




   1: IList<CustomAttributeData> data = 


   2:     CustomAttributeData.GetCustomAttributes(


   3:     typeof (System.Web.UI.Page));




Następnie iterujemy przez wszystkie dane atrybutów i wyświetlany uzyskane informacje:




   1: foreach (var attributeData in data)


   2: {


   3:     Console.WriteLine("Typ atrybutu: {0}",attributeData.Constructor.DeclaringType.FullName);


   4:     Console.WriteLine(" Argumenty konstruktora:");


   5:     for (int i = 0; i < attributeData.ConstructorArguments.Count; i++)


   6:     {


   7:         ParameterInfo param = attributeData.Constructor.GetParameters()[i];


   8:         object value = attributeData.ConstructorArguments[i].Value;


   9:         Console.WriteLine("  {0} = {1}", param.Name, value);


  10:     }


  11:     Console.WriteLine(" Nazwane argumenty:");


  12:     foreach (var arg in attributeData.NamedArguments)


  13:     {


  14:         Console.WriteLine("  {0} = {1}", 


  15:             arg.MemberInfo.Name, 


  16:             arg.TypedValue.Value);


  17:     }


  18: }




Oto wynik działania kodu:


custattrdata


Czyli dokładnie te informacje, których się spodziewaliśmy.




Wydajność



Pomimo, że CustomAttributeData nie tworzy instancji obiektów, to koszt (czas) jego uzyskania jest znacznie większy niż w przypadku np. Type.GetCustomAttributes. Justin Rogers na swoim blogu prezentuje własne badanie, które wykazało różnicę około czterokrotną. Tak znaczącą koszt może wynikać chociażby z faktu, iż CustomAttributeData musi pobrać bardzo wiele metadanych (ConstructorInfo oraz PropertyInfo dla każdego nazwanego argumentu). Po dokładniejsze informacje odsyłam do wyżej wspomnianego posta.



Kiedy może się taki sposób refleksji przydać?



1. Tryb ReflectionOnly



Assembly, które definiuje typ, mamy załadowane w trybie ReflectionOnly. Przykładowo:





   1: Assembly.ReflectionOnlyLoad("mscorlib");


   2: Type reflectionOnlyString = 


   3:     Type.ReflectionOnlyGetType(typeof(string).AssemblyQualifiedName, 


   4:     false, false);




W takiej sytuacji użycie CustomAttributeData jest jedyną możliwością uzyskania informacji o atrybutach, gdyż w trybie ReflectionOnly żaden kod zdefiniowany w assembly nie może zostać uruchomiony. Metoda Type.GetCutomAttibutes() nie może zostać uruchomiona, gdyż tworzy ona instancje wszystkich atrybutów.





2. Dostęp do wszystkich informacji podanych do kontruktora atrybutu



Zdarzają się takie sytuacje, że potrzebujemy uzyskać dostęp bezpośrednio do danych przekazanych w konstruktorze atrybutu. Niestety, nie zawsze twórcy udostępniają je jako publiczne pola/właściwości lub też są dostępne w sposób pośredni. Prosty przykład - rozważmy przykład atrybutu, którego zadaniem jest walidacja czy właściwość, którą opisuje, nie jest nullem lub pustym stringiem (taka mikro wersja Validation Application Block):





   1: public class NotNullOrEmptyAttribute: Attribute


   2: {


   3:     public string ErrorMessage { get; private set; }


   4:     private bool _negated;


   5:  


   6:     public NotNullOrEmptyAttribute(string fieldName, bool negated)


   7:     {


   8:         ErrorMessage = String.Format(


   9:             "Value for field {0} cannot be a null value or empty string.", 


  10:             fieldName);


  11:         _negated = negated;


  12:     }


  13:  


  14:     public bool IsValid(string value)


  15:     {


  16:         return String.IsNullOrEmpty(value) == _negated;


  17:     }


  18: }



Zastosowanie takiego atrybutu mogłoby wyglądać następująco:



   1: public class User


   2: {


   3:     [NotNullOrEmpty("user name", false)]


   4:     public string UserName { get; set; }


   5:  


   6:     //...


   7: }




Oczywiście w takiej sytuacji dokonanie walidacji, czy dana wartość jest poprawna dla danej właściwości, będzie dziecinnie proste. Gorzej jednak, gdy chcemy wykorzytać ten atrybut do innych celów niż przewidział twórca. Na przykład, potrzebujemy utworzyć stronę ASP.NET, która edytuje dane użytkownika i która automatycznie na podstawie atrybutu wstawia walidator RequiredFieldValidator. Wówczas potrzebujemy wartości podanych do kontruktora atrybutu, czyli fieldName oraz negated. Dostęp do tych danych możemy uzyskać:




  • nieelegancko i w sposób wrażliwy na przyszłe zmiany - "wydłubując" z ErrorMessage wartość fieldName


  • modląc się, że docelowo nasz kod będzie miał odpowiednio wysokie uprawnienia - za pomocą refleksji odczytać prywatną wartość pola _negated (jak? polecam wpis na blogu Maćka Aniserowicza)



Możemy jednak elegancko zastosować właśnie CustomAttributeData:





   1: IList<CustomAttributeData> data = 


   2:     CustomAttributeData.GetCustomAttributes(


   3:     typeof (User).GetProperty("UserName"));


   4:  


   5: CustomAttributeData nullOrEmptyData =


   6:     data.First(d => d.Constructor.DeclaringType == typeof (NotNullOrEmptyAttribute));


   7:  


   8: string fieldName = (string)nullOrEmptyData.ConstructorArguments[0].Value;


   9: bool isNegated = (bool) nullOrEmptyData.ConstructorArguments[1].Value;




Mała uwaga - jeżeli dodatkowo klasę User mamy załadowaną w trybie ReflectionOnly to porównanie




d.Constructor.DeclaringType == typeof (NotNullOrEmptyAttribute)



nigdy nie zwróci true (bo w trybie ReflectionOnly typ NullOrEmptyAttribute to inny typ niż NullOrEmptyAttribute w "normalnym" trybie). Wówczas wyjściem jest sprawdzenie nazwy typu (najbezpieczniej razem z kwalifikowaną nazwą assembly):




d.Constructor.DeclaringType.AssemblyQualifiedName == 
"CustomAttributeDataTest.NotNullOrEmptyAttribute, CustomAttributeDataTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

piątek, 9 maja 2008

Na dobry początek - kolejny offtopic

Zamiast pisać sporo o sobie postanowiłem wrzucić dwa klipy, które zawsze poprawiają mi humor (choć raczej nie narzekam na chandry wszelkiej maści).

  1. Pierwszy to klip "Body Movin'" od Beastie Boys (album "Hello Nasty" 1998):

  2. Drugi to utwór z filmu "Autostopem przez galaktykę" ("The Hitchhiker's Guide to the Galaxy"), który jeśli chodzi o typ humoru, jest moim absolutnym faworytem:

O mnie, blogu i sensie wszystkiego

Ponieważ każdy blog musi zaczynać się wpisem, w którym autor się przedstawia i którego nikt nie czyta, to ja nie będę gorszy. A więc trzy słowa...:

Jestem:

  • studentem informatyki na Politechnice Łódzkiej (właściwie to już tylko jedną nogę),
  • programistą C#,
  • autorem tego bloga.

Zajmuję się/zajmowałem się:

  • aktualnie pracuję dla firmy Webstruments,
  • wcześniej brałem udział w ITCore (falstart numer 1) jako architekt; tutaj nadmienię, że pomimo porażku i wątpliwej jakości efektów, to jestem dumny z tego, że miałem przyjemność uczestniczyć w tym projekcie i w tym zespole,
  • a jeszcze wcześnie udzielałem się w projekcie CodeGuru jako programista/lider zespołu.

Chcę:

  • aby ten blog był w miarę ciekawy,
  • kiedyś wiedzieć choć połowę tego co autor tego blogu.

A co do sensu wszystkiego, to jest to oczywiście: 0x2a (zagadka dla dociekliwych).