Czas na proste demo AOP przy użyciu PostSharp i IL Code Weaving.
W poprzednim wpisie omówiłem na czym polega AOP. Jeśli jeszcze wszystkiego nie rozumiesz, to spokojnie dopiero patrząc na konkretne przykłady, sprawa z AOP staje się jasna jak żarówka.
Projekt Demo zaczynamy od utworzenia nowego projektu w Visual Studio. Demo będzie proste, dlatego wystarczy nam aplikacja konsolowa.
Czas do naszego projektu dodać bibliotekę PostSharp.
W oknie NuGet wpisz “PostSharp” i zainstaluj paczkę do swojego projektu.
PostSharp jest płatnym narzędziem. Przykłady AOP, które zastosuję jednak nie wymagają kompletnej wersji. Dlatego w oknie wybierz “Use PostSharp Express”.
Na temat tego, co i jaka wersja PostSharpa potrafi, możesz przeczytać tutaj:
https://www.postsharp.net/purchase
Teraz, gdy referencja do PostSharpa została zainstalowana możemy napisać klasy biznesowe, które są tak zawansowane, że wyświetlają napis HelloWorld.
public class MotherFBussinesClass
{
public void Method1()
{
Console.WriteLine("I am MotherFBussinesClass");
}
}
public class AnotherBussinesClass
{
public void Method1()
{
Console.WriteLine("I am AnotherBussinesClass");
}
}
Użyjemy je w prosty sposób. Stworzymy obiekty biznesowe i wywołamy ich metody.
namespace PostSharpHello
{
class Program
{
static void Main(string[] args)
{
var obj = new MotherFBussinesClass();
var obj2 = new AnotherBussinesClass();
obj.Method1();
obj2.Method1();
}
}
}
Gdy spróbujesz skompilować aplikację z bibliotekami PostSharp. Visual Studio przypomni ci, że same biblioteki nie wystarczą i trzeba zainstalować dodatek do Visual Studio.
W końcu jak kompilator .NET ma wstrzykiwać po procesie kompilacyjnym kod, jeśli on nie ma takiego wbudowanego mechanizmu.
Zainstaluj wiec dodatek do Visual Studio.
https://visualstudiogallery.msdn.microsoft.com/a058d5d3-e654-43f8-a308-c3bdfdd0be4a
Po uruchomieniu zobaczysz okno Tutorialu, ale zignoruj go na razie.
Teraz kod może się skompilować.
Na razie nie dzieje się żadna magia AOP, więc ją dodajmy.
Stwórzmy nasz pierwszy Aspekt.
Klasa Aspektowa PostSharpa musi być zserializowana. Wynika to z tego, że PostSharp dodaje aspekt w procesie po kompilacyjnym. Aspekty muszą być istnieć pomiędzy czasem kompilacji i czasem uruchomienia.
using PostSharp.Aspects;
using System;
namespace PostSharpHello
{
[Serializable]
public class MyAspect : OnMethodBoundaryAspect
{
}
}
Nasz pierwszy aspekt dziedziczy po klasie “OnMethodBoundaryAspect”. Oznacza to, że możemy teraz się bawić kodem wewnątrz każdej metody w naszej aplikacji.
Zacznij więc krojenie i ucinanie.
Gdzie możemy to zrobić. Zobaczmy jakie metody możemy przeciążyć.
Każda metoda ma punkt łączenia jak:
- Przed wykonaniem metody - OnEntry
- Po wykonaniu metody – OnExit
- Kiedy metoda wyrzuci wyjątek – OnException
- Kiedy metoda wykona się bez wyjątku – OnSuccess
- Kiedy metoda wraca do wykonywania po słowie kluczowym Yield albo await – onResume
- Kiedy metoda następuje na słowo kluczowe yeild i await – OnYeild
Jest ich trochę więcej niż omówiłem w poprzednim wpisie. Szerszy opis metod znajduje się tutaj.
http://doc.postsharp.net/t_postsharp_aspects_onmethodboundaryaspect
PostSharp ma dobrą dokumentację.
Wykorzystajmy więc metody OnEntry i OnExist i dodajmy kod, który się wykona przed wywołaniem metod biznesowych i po.
[Serializable]
public class MyAspect : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
Console.WriteLine("Before Method " + args.Method.Name);
}
public override void OnExit(MethodExecutionArgs args)
{
Console.WriteLine("After Method " + args.Method.Name);
}
}
Teraz, gdy mamy klasę Aspektu musimy jakoś powiedzieć PostSharpowi, że chcemy je wywołać w naszych metodach.
Dla uproszczenia skorzystałem z atrybutów, ale uwaga PostSharp nie jest takim słabym narzędziem, byśmy musieli wklejać atrybuty do każdej istniejącej metody w naszym kodzie. Na razie chcę utrzymać przykład w prostocie.
public class MotherFBussinesClass
{
[MyAspect]
public void Method1()
{
Console.WriteLine("I am MotherFBussinesClass");
}
}
public class AnotherBussinesClass
{
[MyAspect]
public void Method1()
{
Console.WriteLine("I am AnotherBussinesClass");
}
}
Jak widzimy Aspekty działają.
Teraz pytanie co mogę fajnego zrobić wewnątrz tych metod jak OnEntry.
Parametr args zawiera informację:
- O metodzie
- O argumentach przesłanych do metody
- O wyjątku jeśli wystąpił
- O wartości zwracanej jeśli ona jest
- i o samej instancji klasy, która posiada tą metodę.
To wszystko daje spore możliwości do cięcia kodu na Aspekty.
Oto ulepszony kod klasy Aspektowej. Korzystając z właściwości Method jestem w stanie pobrać informacje na temat nazwy klasy, która wywołuje tę metodę. Mogę też pobrać informację na temat modułu, w którym znajduje się ta metoda.
[Serializable]
public class MyAspect : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine("Before Method " +
args.Method.DeclaringType.Name + " "+ args.Method.Name
+ "in " + args.Method.Module.Name);
Console.ForegroundColor = ConsoleColor.Gray;
}
public override void OnExit(MethodExecutionArgs args)
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("After Method " +
args.Method.DeclaringType.Name + " " + args.Method.Name
+ "in " + args.Method.Module.Name);
Console.ForegroundColor = ConsoleColor.Gray;
}
}
Tekst ciemno i jasno żółty obrazuje działanie kodu w klasach aspektowych.
Ten prosty przykład demo jest imponujący. Zauważ, że dodaliśmy kod do metod biznesowych, wcale ich nie zmieniając.
Musiałeś dodać atrybut, ale nawet to nie jest potrzebne.
Oczywiście PostSharp i IL Code Weaving nie jest jedyną techniką implementacyjną AOP.
Mamy jeszcze rozwiązanie CastleWindsor polegające na tworzeniu dynamicznych dekoratorów;