ABONAMENTE VIDEO REDACȚIA
RO
EN
NOU
Numărul 150
Numărul 149 Numărul 148 Numărul 147 Numărul 146 Numărul 145 Numărul 144 Numărul 143 Numărul 142 Numărul 141 Numărul 140 Numărul 139 Numărul 138 Numărul 137 Numărul 136 Numărul 135 Numărul 134 Numărul 133 Numărul 132 Numărul 131 Numărul 130 Numărul 129 Numărul 128 Numărul 127 Numărul 126 Numărul 125 Numărul 124 Numărul 123 Numărul 122 Numărul 121 Numărul 120 Numărul 119 Numărul 118 Numărul 117 Numărul 116 Numărul 115 Numărul 114 Numărul 113 Numărul 112 Numărul 111 Numărul 110 Numărul 109 Numărul 108 Numărul 107 Numărul 106 Numărul 105 Numărul 104 Numărul 103 Numărul 102 Numărul 101 Numărul 100 Numărul 99 Numărul 98 Numărul 97 Numărul 96 Numărul 95 Numărul 94 Numărul 93 Numărul 92 Numărul 91 Numărul 90 Numărul 89 Numărul 88 Numărul 87 Numărul 86 Numărul 85 Numărul 84 Numărul 83 Numărul 82 Numărul 81 Numărul 80 Numărul 79 Numărul 78 Numărul 77 Numărul 76 Numărul 75 Numărul 74 Numărul 73 Numărul 72 Numărul 71 Numărul 70 Numărul 69 Numărul 68 Numărul 67 Numărul 66 Numărul 65 Numărul 64 Numărul 63 Numărul 62 Numărul 61 Numărul 60 Numărul 59 Numărul 58 Numărul 57 Numărul 56 Numărul 55 Numărul 54 Numărul 53 Numărul 52 Numărul 51 Numărul 50 Numărul 49 Numărul 48 Numărul 47 Numărul 46 Numărul 45 Numărul 44 Numărul 43 Numărul 42 Numărul 41 Numărul 40 Numărul 39 Numărul 38 Numărul 37 Numărul 36 Numărul 35 Numărul 34 Numărul 33 Numărul 32 Numărul 31 Numărul 30 Numărul 29 Numărul 28 Numărul 27 Numărul 26 Numărul 25 Numărul 24 Numărul 23 Numărul 22 Numărul 21 Numărul 20 Numărul 19 Numărul 18 Numărul 17 Numărul 16 Numărul 15 Numărul 14 Numărul 13 Numărul 12 Numărul 11 Numărul 10 Numărul 9 Numărul 8 Numărul 7 Numărul 6 Numărul 5 Numărul 4 Numărul 3 Numărul 2 Numărul 1
×
▼ LISTĂ EDIȚII ▼
Numărul 21
Abonament PDF

AOP folosind .NET stack

Radu Vunvulea
Solution Architect
@iQuest



PROGRAMARE

În cele ce urmează vom discuta despre AOP și despre cum putem implementa propria noastră stivă (stack) AOP utilizând caracteristicile .NET Core. Acronimul vine de la Aspect Oriented Programming și este o altă paradigmă de programare cu scopul principal de a crește modularitatea unei aplicații. AOP încearcă să atingă acest țel permițând separarea aspectelor/relațiilor secante (cross-cutting concerns).

Fiecare parte a aplicației este împărțită în unități distincte bazate pe funcționalitate (aspecte/relații). Bineînțeles, chiar dacă nu utilizăm această paradigmă, vom încerca să avem o separare clară a funcționalităților. Dar în AOP toate funcționalitățile sunt separate, chiar și cele pe care în OOP le acceptăm ca fiind secante.

Un exemplu bun în acest caz este audit (controlul) și logging (jurnalizare). În mod normal, dacă utilizăm OOP pentru a dezvolta o aplicație care necesită logging și audit, vom avea într-o formă sau alta diferite apelări ale mecanismului de logging din codul nostru. În OOP, acest lucru poate fi acceptat, deoarece aceasta este singura modalitate de a scrie logaritmi, de a prelucra și așa mai departe.

Când folosim AOP, implementarea sistemului de logging sau audit va trebui plasată într-un modul separat. În plus, vom avea nevoie de o modalitate de a scrie informația de logging fără a scrie cod în alte module care fac apelarea în sine a sistemului de logging.

Există diferite opțiuni care pot fi utilizate în AOP pentru a soluționa această problemă. Depinde de tipul de tehnologie pe care îl utilizați, de tipul de stack (stivă) pe care doriți să-l folosiți și așa mai departe. Am putea utiliza atribute (adnotații) ale metodelor și claselor care ar activa logging și audit. O altă abordare este configurarea unor înlocuitori care vor fi folosiți de către sistem drept implementarea reală a unei funcționalități. Acest înlocuitor va fi capabil să scrie logs și să facă apelarea implementării de bază.

Interceptarea

Aproape toate cadrele disponibile pentru AOP sunt în jurul interceptării. Folosind interceptarea, dezvoltatorii pot specifica ce metode trebuie interceptate și ce ar trebui să se întâmple în acest caz. Prin interceptare, noi putem chiar să adăugăm noi câmpuri, proprietăți , metode sau chiar să modificăm implementarea curentă.

Cum este implementat?

De obicei, există două moduri oferite de diferitele cadre care oferă suport AOP în timpul de execuție și în timpul de compilare.

Când cadrul oferă suport AOP în timpul execuției, înseamnă că va crea în timp real diverși reprezentanți care vor redirecționa apelările noastre. Aceasta ne va da o mare flexibilitate, făcându-ne capabili să modificăm în timpul execuției comportamentul aplicației noastre.

O altă abordare este în timpul de compilare. Acest tip de cadre sunt de obicei integrate cu IDE și cu mediul de dezvoltare. În timpul de compilare, ele vor aduce modificări codului nostru și vor introduce apelările diferitelor functionalități. În final, va rezulta același cod ca și când am apela codul prin metoda noastră, dar codul pe care trebuie să îl întreținem este mai simplu, curat și cu toate aspectele clar separate.

Costuri

Când cadrul AOP folosește reprezentanți în timp real, performanța aplicației noastre poate să scadă. Acest lucru se întâmplă deoarece există cineva la mijloc care interceptează apelările noastre și face anumite acțiuni. La acest nivel de obicei se folosește reflecția, iar noi știm cu toții că reflecțiile costă mult din perspectiva procesorului.

A avea un cod care se modifică în timpul de compilare, înseamnă că putem avea anumite probleme când este nevoie să corectăm codul și să găsim o problemă. Aceasta se întâmplă deoarece în timpul de execuție nu rămâi doar cu codul tău, ci sfârșești prin a avea codul tău original din relația de bază, plus codul din a doua relație de care ai avut nevoie în relația de bază și cu codul cadru AOP care face redirecționarea.

După cum putem vedea în diagrama de mai sus, numai primul și ultimul pas fac parte din ciclul normal. Restul sunt adăugați de cadrul AOP.

Componentele de bază ale AOP

Join Points (Puncte de întâlnire)

Un punct de întâlnire este reprezentat de punctul din cod unde este necesar să punem în aplicare operațiile propuse de noi. Acesta este un punct din cod unde poți face o apelare a unei alte funcționalități într-un mod foarte simplu. În general, cadrele care susțin AOP utilizează metode, clase și proprietăți care reprezintă principalele lor puncte de întâlnire.

De exemplu, înainte și/sau în timpul apelării unei metode, poți executa codul tău obișnuit. Același lucru se poate întâmpla pentru clase și proprietăți. În general, vei descoperi că conceptele AOP sunt foarte simple, dar destul de dificil de implementat păstrând un cod curat și simplu.

Pointcuts

Definesc o modalitate de a specifica un punct de întâlnire în sistemul tău. Aceasta oferă dezvoltatorului posibilitatea de a specifica și identifica un punct de întâlnire în sistem unde dorim să facem o anume apelare. Acest lucru poate fi realizat în diferite feluri, de la atribute (adnotații) la configurații diverse (în fișiere, în cod și multe altele).

Sfatul

Sfatul se referă la codul pe care vrem să îl executăm când ajungem la un punct de întâlnire. În principiu, acesta este reprezentat de codul care este apelat în jurul unui punct de întâlnire.

De exemplu, înainte ca o metodă să fie apelată, dorim să scriem niște informații pe traseu.

Aspect

Aspectul este format din două elemente diferite - pointcut și sfatul. Combinarea acestor două elemente formează un aspect. În general, când vrem să utilizăm AOP, avem o locație unde dorim să executăm codul și codul obișnuit care vrem să fie executat.

.NET Stacks

Există moduri diferite de a implementa AOP în .NET. Pe piață, veți găsi numeroase cadre care oferă acest suport, o parte dintre ele sunt gratuite.

Unity

Unity oferă suport pentru AOP într-o anumită parte a aplicației. În general, Unity oferă suport pentru scenariile cele mai comune, cum ar fi tratarea excepțiilor, logging, protecție sau acces la date.

PostSharp

Acesta este unul dintre cele mai cunoscute cadre în lumea .NET pentru AOP. Nu este un cadru gratuit, dar este plin de caracteristici utile și este 100% integrat cu mediul de dezvoltare.

Aspect.NET

Acesta este un instrument gratuit care poate fi găsit pe Codeplex. El oferă funcționalitățile de bază necesare pentru a dezvolta o aplicație utilizând paradigma AOP.

Enterprise Library

Această bibliotecă oferă de asemenea suport pentru AOP, furnizând diferite capabilități precum autorizarea, tratarea excepțiilor, validare, logging și calculator pentru performanță. Este destul de asemănătoare cu funcționalitățile oferite de Unity.

AspectSharp

Aceasta este o altă stivă similară cu Aspect.NET. În ambele cazuri, trebuie să știți că aveți acces numai la funcționalitățile de bază ale paradigmei AOP.

Castle Project - Dynamic Proxy

Asemănător cu AspectSharp. Aceasta este o bibliotecă susținută de către comunitate și veți putea găsi mult suport și informații utile pe diferite forumuri și bloguri.

Din perspectiva mea, instrumentul care oferă toate caracteristicile de care ai nevoie atunci când vrei să utilizezi AOP este PostSharp. Dar, în funcție de nevoile proprii, ar trebui să încercați să identificați instrumentul care satisface nevoile voastre.

Ce oferă .NET Code

Vestea bună este că noi putem utiliza AOP fără niciun fel de instrument. Poate fi mai complicat, dar ceea ce poți realiza utilizând .NET API este destul de interesant. În următoarea parte a articolului vom vedea cum putem folosi clasa RealProxy pentru a intercepta apelările metodei și pentru a injecta comportament obișnuit. Clasa RealProxy poate fi găsită în stiva .NET Core.

Cea mai importantă metodă a RealProxy este "Invoke"(Invocarea). Această metodă este apelată de fiecare dată când este apelată o metodă din clasa ta specifică. De aici, poți accesa numele metodei, parametrii și poți apela metoda ta reală sau una falsă.

Este important de știut că aceasta va funcționa numai când folosiți și interfețele.

În următorul exemplu, vom vedea cum putem implementa un mecanism de profilare obișnuit, utilizând clasa RealProxy.

Primul pas este să creăm un atribut obișnuit, care acceptă un mesaj obișnuit care va fi scris atunci când scriem durata pe traseu.

public class DurationProfillingAttribute : Attribute
{
   public DurationProfillingAttribute(string message)
   {
       Message = message;
   }

   public DurationProfillingAttribute()
   {
       Message = string.Empty;
   }

   public string Message { get; set; }
}

Apoi avem nevoie de o clasă generală care extinde RealProxy și calculează durata apelării. În metoda Invocare va trebui să utilizăm un cronometru care va calcula cât durează o apelare. La acest nivel, putem verifica dacă o metodă specifică este decorată cu atributul nostru.

public class DurationProfilingDynamicProxy : RealProxy
{
    private readonly T _decorated;

    public DurationProfilingDynamicProxy(T decorated)
        : base(typeof(T))
    {
        _decorated = decorated;
    }

    public override IMessage Invoke(IMessage msg)
    {
    IMethodCallMessage methodCall = 
		(IMethodCallMessage)msg;
    
MethodInfo methodInfo = methodCall.MethodBase as 
		MethodInfo;

DurationProfillingAttribute profillingAttribute = 
(DurationProfillingAttribute)methodInfo.
GetCustomAttributes(typeof(
DurationProfillingAttribute)).FirstOrDefault();

// Method don"t needs to be measured. 
if (profillingAttribute == null)
{
    return NormalInvoke(methodInfo, methodCall);
}

return ProfiledInvoke(methodInfo, methodCall,
		 profillingAttribute.Message);
}

private IMessage ProfiledInvoke(MethodInfo methodInfo, IMethodCallMessage methodCall, string profiledMessage)
{
   Stopwatch stopWatch = null;
   try
   {
    stopWatch = Stopwatch.StartNew();
    var result = InvokeMethod(methodInfo, methodCall);
  	          stopWatch.Stop();

    WriteMessage(profiledMessage, 
	methodInfo.DeclaringType.FullName, 
	methodInfo.Name, stopWatch.Elapsed);

     return new ReturnMessage(result, null, 0,
                methodCall.LogicalCallContext,
		methodCall);


     }
     catch (Exception e)
     {
       if (stopWatch != null
            && stopWatch.IsRunning)
         {
            stopWatch.Stop();
          }
          return new ReturnMessage(e, methodCall);
      }
    }

  private IMessage NormalInvoke(MethodInfo methodInfo, 
		IMethodCallMessage methodCall)
   {
   try
   {
    var result = InvokeMethod(methodInfo, methodCall);

    return new ReturnMessage(result, null, 0,
          methodCall.LogicalCallContext, methodCall);
    }
    catch (Exception e)
    {
          return new ReturnMessage(e, methodCall);
      }
    }

   private object InvokeMethod(MethodInfo methodInfo, 
	  IMethodCallMessage methodCall)
    {
    object result = methodInfo.Invoke(_decorated, 
		methodCall.InArgs);
       
    return result;
    }


   private void WriteMessage(string message, string 
	className, string methodName, 
	TimeSpan elapsedTime)
    {
    Trace.WriteLine(string.Format("
	Duration Profiling: "{0}" for "{1}.{2}" 
	Duration:"{3}"", message, 		
	className,methodName, elapsedTime));
    }

Am putea avea o altă abordare aici, calculând durata pentru toate metodele din clasă. Puteți găsi mai jos clasele utilizate pentru a testa implementarea. Folosind metoda "GetTransparentProxy" putem obține o referință la interfața noastră.

class Program
{
 static void Main(string[] args)
  {
   DurationProfilingDynamicProxy 
   fooDurationProfiling = new
     DurationProfilingDynamicProxy(new Foo());
    
    IFoo foo = (IFoo)fooDurationProfiling.
	GetTransparentProxy();

        foo.GetCurrentTime();
        foo.Concat("A", "B");
        foo.LongRunning();
        foo.NoProfiling();
    }
}

public interface IFoo
{
[DurationProfilling("Some text")]
DateTime GetCurrentTime();

[DurationProfilling]
string Concat(string a, string b);

[DurationProfilling("After 2 seconds")]
void LongRunning();

string NoProfiling();
}

public class Foo : IFoo
{
public DateTime GetCurrentTime()
{
    return DateTime.UtcNow;
}

public string Concat(string a, string b)
{
    return a + b;
}

public void LongRunning()
{
    Thread.Sleep(TimeSpan.FromSeconds(2));
}

public string NoProfiling()
{
    return "NoProfiling";
}

}

Concluzie

În concluzie, putem spune că AOP ne poate face viața mai ușoară. Aceasta nu înseamnă că de acum o vom folosi în toate proiectele. AOP trebuie utilizată numai acolo unde are sens și de obicei poate fi foarte utilă când lucrăm la un proiect care este foarte complicat, cu multe module și funcționalități.

În următorul articol vom descoperi cum putem utiliza Unity pentru a implementa AOP.

NUMĂRUL 149 - Development with AI

Sponsori

  • Accenture
  • BT Code Crafters
  • Accesa
  • Bosch
  • Betfair
  • MHP
  • BoatyardX
  • .msg systems
  • P3 group
  • Ing Hubs
  • Cognizant Softvision
  • Colors in projects