Podstawowa implementacja atrybutu AOP like przy użyciu standard.NET Framework [duplikat]

Możliwy duplikat:
metoda C# wrap poprzez atrybuty

Chciałbym osiągnąć taką funkcjonalność:

[Atomic]
public void Foo()
{           
    /* foo logic */
}

Gdzie [Atomic] atrybut jest atrybutem, który zawija logikę funkcji w zakres transakcji:

using(var scope = new TransactionScope())
{
    /* foo logic */
    scope.Complete();
}

Jak napisać taki atrybut?

Zadałem wcześniej w zasadzie to samo pytanie , wiem, że można to zrobić za pomocą AOP, ale nie wspomniałem, że szukam jakiegoś najprostszego dowodu koncepcji implementacja lub Pomocne artykuły, które mogą mi pomóc napisać to przy użyciu czystego. NET Framework (przypuszczam, że przy użyciu typów RealProxy i MarshalByRefObject, o których czytałem przeglądanie powiązanych pytań).

Muszę rozwiązać dokładnie ten pokazany przykład. Wydaje się to podstawową rzeczą, więc chcę się nauczyć, jak to zrobić, zaczynając od zera. Na razie nie musi być bezpieczny i elastyczny.

Author: Community, 2013-01-13

4 answers

Wygląda to na podstawową rzecz...
Jest to jedna z (wielu) rzeczy, które są proste do zrozumienia, ale wcale nie proste do wdrożenia.

Zgodnie z odpowiedzią Oded , atrybuty w. Net nie robią nic . Istnieją tylko po to ,aby inni programiści (lub programiści) mogli później na nie spojrzeć. Pomyśl o tym jak o wymyślnym komentarzu.

Mając to na uwadze, możesz napisać swój atrybut w ten sposób

public class AtomicAttribute : Attribute { } 
Teraz najtrudniejsza część, musisz napisz jakiś kod, aby zeskanować ten atrybut i zmienić jego zachowanie.

Biorąc pod uwagę, że C# jest językiem skompilowanym, a biorąc pod uwagę zasady CLR. NET są teoretycznie 3 sposoby, aby to zrobić

  1. Podłącz się do kompilatora C# i spraw, aby generował inny kod, gdy widzi ten atrybut.
    Wydaje się, że byłoby miło, ale po prostu nie jest to możliwe natychmiast. Być może Roslyn projekt może na to pozwolić w przyszłości, ale na razie nie możesz tego zrobić to.

  2. Napisz coś, co przeskanuje. NET assembly po kompilator C# przekonwertuje go do MSIL i zmieni MSIL.
    To jest w zasadzie to, co robi PostSharp . Skanowanie i przepisywanie MSIL jest twarde . Istnieją biblioteki takie jak Mono.Co może pomóc, ale to wciąż bardzo trudny problem. Może również zakłócać działanie debuggera, itp.

  3. Użyj. NET Profilowanie API do monitorowania programu podczas jest uruchomiony i za każdym razem, gdy widzisz wywołanie funkcji z tym atrybutem, przekierowuj je do innej funkcji wrappera.
    Jest to prawdopodobnie najprostsza opcja (chociaż nadal jest to bardzo trudne), ale wadą jest to, że twój program teraz musi być uruchomiony pod profilerem. Może to być w porządku na komputerze deweloperskim, ale spowoduje to ogromny problem, jeśli spróbujesz go wdrożyć. Ponadto, istnieje prawdopodobieństwo, że będzie duży hit wydajności przy użyciu tego podejścia.

W moim opinia, najlepiej jest utworzyć funkcję wrapper, która ustawia transakcję, a następnie przekazać ją lambda, która wykonuje rzeczywistą pracę. Tak:

public static class Ext 
{
    public static void Atomic(Action action) 
    {
        using(var scope = new TransactionScope()) 
        {
            action();
            scope.Commit();
        }
    }
}

.....

using static Ext; // as of VS2015

public void Foo()
{
    Atomic(() => {
        // foo logic
    }
}

Wymyślnym terminem informatycznym na to jest Programowanie wyższego rzędu

 21
Author: Orion Edwards,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:26

Atrybuty to metadane - czyli wszystkie .

Istnieje wiele narzędzi, które mogą skorzystać z takich metadanych, ale takie narzędzia muszą być świadome atrybutu.

Narzędzia AOP, takie jak PostSharp, odczytują takie metadane, aby wiedzieć, co i gdzie wpleść aspekty do kodu.

W skrócie-samo napisanie AtomicAttribute da ci nic - będziesz musiał przekazać skompilowany assembly przez narzędzie, które wie o tym atrybutie i zrobić " coś" do niego w celu osiągnięcia AOP.

 10
Author: Oded,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-13 19:36:01

To wcale nie jest rzecz podstawowa. Żaden dodatkowy kod nie jest uruchamiany tylko dlatego, że metoda ma atrybut, więc nie ma gdzie umieścić twojego TransactionScope kodu.

To, co musisz zrobić, to po uruchomieniu aplikacji użyj reflection, aby iterować nad każdą metodą na każdej klasie w Twoim złożeniu i znajdź metody oznaczone AtomicAttribute, a następnie napisz własny proxy wokół tego obiektu. Potem jakoś dostać wszystko inne, aby wywołać proxy zamiast prawdziwej implementacji, być może za pomocą zależności ramy wtryskowe.

Większość frameworków AOP robi to w czasie budowania. Na przykład PostSharp działa po zbudowaniu zestawu przez VisualStudio. Skanuje twój zespół i przepisuje kod IL, aby zawierał proxy i przechwytywacze AOP. W ten sposób zespół jest ustawiony na działanie, ale IL zmienił się od tego, co pierwotnie napisałeś.

 4
Author: CodingWithSpike,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-13 20:12:53

Może rozwiązać wszystkie obiekty używając kontenera IoC? Możesz skonfigurować interceptors dla swoich typów i w nich sprawdzić, czy wywołana metoda jest ozdobiona tym atrybutem. Możesz buforować te informacje, dzięki czemu nie musisz używać refleksji przy każdym wywołaniu metody.

Więc kiedy to zrobisz:

var something = IoC.Resolve<ISomething>();

something to nie obiekt, który zaimplementowałeś, ale proxy. W tym proxy możesz robić co chcesz przed i po wywołaniu metody.

 3
Author: Piotr Perak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-13 20:25:37