AOP mit .NET



12.04.2012
Dipl.-Inf. (FH) Johannes Hoppe
Johannes Hoppe
ASP.NET MVC Webentwickler
  www.johanneshoppe.de
01
Architektur und Patterns
Patterns
software craftsmanship
Business Code



public class CustomerProcesses
{
    public void RentBook( int bookId, int customerId )
    {
        Book book = Book.GetById( bookId );
        Customer customer = Customer.GetById( customerId );

         book.RentedTo = customer;
         customer.AccountLines.Add(
          string.Format( "Rental of book {0}.", book ), book.RentalPrice
);
         customer.Balance -= book.RentalPrice;
     }
}
Business Code
Business Code



public class CustomerProcesses
{
    public void RentBook( int bookId, int customerId )
    {
        Book book = Book.GetById( bookId );
        Customer customer = Customer.GetById( customerId );

         book.RentedTo = customer;
         customer.AccountLines.Add(
          string.Format( "Rental of book {0}.", book ), book.RentalPrice
);
         customer.Balance -= book.RentalPrice;
     }
}
Business Code
+ Logging
                internal class CustomerProcesses
                {
                    private static readonly TraceSource trace =
                        new TraceSource( typeof (CustomerProcesses).FullName );

                        public void RentBook( int bookId, int customerId )
                        {
                           trace.TraceInformation(
                                "Entering CustomerProcesses.CreateCustomer( bookId = {0},
                                 customerId = {1} )",
                                bookId, customerId );
                           try
                           {
                                Book book = Book.GetById( bookId );
                                Customer customer = Customer.GetById( customerId );

                                book.RentedTo = customer;
                                customer.AccountLines.Add(
                                    string.Format( "Rental of book {0}.", book ), book.RentalPrice );
                                customer.Balance -= book.RentalPrice;

                                trace.TraceInformation(
                                  "Leaving CustomerProcesses.CreateCustomer( bookId = {0}, customerId = {1} )",
                                  bookId, customerId );
                            }
                            catch ( Exception e )
                            {
                                trace.TraceEvent( TraceEventType.Error, 0,
                                                  "Exception: CustomerProcesses.CreateCustomer(
                                                  bookId = {0}, customerId = {1} ) failed : {2}",
                                                  bookId, customerId, e.Message );
                                 throw;
                            }
                    }
                }
Business Code
+ Logging
                   internal class CustomerProcesses

+ Vorbedingungen   {
                       private static readonly TraceSource trace =
                           new TraceSource(typeof(CustomerProcesses).FullName);

                       public void RentBook(int bookId, int customerId)
                       {
                           if (bookId <= 0) throw new ArgumentOutOfRangeException("bookId");
                           if (customerId <= 0) throw new ArgumentOutOfRangeException("customerId");

                           trace.TraceInformation(
                               "Entering CustomerProcesses.CreateCustomer( bookId = {0}, customerId = {1} )",
                               bookId, customerId);

                           try
                           {
                                 Book book = Book.GetById(bookId);
                                 Customer customer = Customer.GetById(customerId);

                                 book.RentedTo = customer;
                                 customer.AccountLines.Add(string.Format("Rental of book {0}.", book),
                                                           book.RentalPrice);
                                 customer.Balance -= book.RentalPrice;

                                 trace.TraceInformation(
                                     "Leaving CustomerProcesses.CreateCustomer( bookId = {0},
                                     customerId = {1} )“, bookId, customerId);
                           }
                           catch (Exception e)
                           {
                               trace.TraceEvent(TraceEventType.Error, 0,
                                      "Exception: CustomerProcesses.CreateCustomer( bookId = {0},
                                       customerId = {1} ) failed : {2}",
                                       bookId, customerId, e.Message);
                               throw;
                           }
                       }
                   }
Business Code
   + Logging        + Transaktionen
   + Vorbedingungen
internal class CustomerProcesses                                                                 ts.Complete();
{                                                                                            }
    private static readonly TraceSource trace =
        new TraceSource(typeof(CustomerProcesses).FullName);                                 break;
                                                                                         }
    public void RentBook(int bookId, int customerId)                                     catch (TransactionConflictException)
    {                                                                                    {
        if (bookId <= 0)                                                                     if (i < 3)
          throw new ArgumentOutOfRangeException("bookId");                                       continue;
        if (customerId <= 0)                                                                 else
          throw new ArgumentOutOfRangeException("customerId");                                   throw;
                                                                                         }
        trace.TraceInformation(                                                      }
            "Entering CustomerProcesses.CreateCustomer( bookId = {0},
            customerId = {1} )“, bookId, customerId);                                trace.TraceInformation(
                                                                                         "Leaving CustomerProcesses.CreateCustomer(
        try                                                                              bookId = {0}, customerId = {1} )",
        {                                                                                bookId, customerId);
              for (int i = 0; ; i++)                                             }
              {                                                                  catch (Exception e)
                  try                                                            {
                  {                                                                  trace.TraceEvent(TraceEventType.Error, 0,
                      using (var ts = new TransactionScope())                          "Exception: CustomerProcesses.CreateCustomer( bookId = {0},
                      {                                                                customerId = {1} ) failed : {2}",
                          Book book = Book.GetById(bookId);                            bookId, customerId, e.Message);
                          Customer customer =                                        throw;
                            Customer.GetById(customerId);                        }
                                                                             }
                         book.RentedTo = customer;
                         customer.AccountLines.Add(                      }
                           string.Format("Rental of book {0}.", book),
                           book.RentalPrice);
                         customer.Balance -= book.RentalPrice;
Business Code
   + Logging        + Transaktionen
   + Vorbedingungen + Exception Handling
internal class CustomerProcesses
{                                                                                                            ts.Complete();
    private static readonly TraceSource trace =                                                          }
        new TraceSource(typeof(CustomerProcesses).FullName);
                                                                                                          break;
    public void RentBook(int bookId, int customerId)                                                  }
    {                                                                                                 catch ( TransactionConflictException )
        if (bookId <= 0) throw new ArgumentOutOfRangeException("bookId");                             {
        if (customerId <= 0)                                                                              if ( i < 3 )
            throw new ArgumentOutOfRangeException("customerId");                                              continue;
                                                                                                          else
        try                                                                                                   throw;
        {                                                                                             }
              trace.TraceInformation(                                                             }
                  "Entering CustomerProcesses.CreateCustomer(
                   bookId = {0}, customerId = {1} )",                                             trace.TraceInformation(
                  bookId, customerId );                                                               "Leaving CustomerProcesses.CreateCustomer(
                                                                                                      bookId = {0}, customerId = {1} )",
              try                                                                                     bookId, customerId );
              {                                                                               }
                    for ( int i = 0;; i++ )                                                   catch ( Exception e )
                    {                                                                         {
                        try                                                                       trace.TraceEvent( TraceEventType.Error, 0,
                        {                                                                          "Exception: CustomerProcesses.CreateCustomer(
                            using ( var ts = new TransactionScope() )                               bookId = {0}, customerId = {1} ) failed : {2}",
                            {                                                                                       bookId, customerId, e.Message );
                                Book book = Book.GetById( bookId );                               throw;
                                Customer customer = Customer.GetById( customerId );           }
                                                                                          }
                               book.RentedTo = customer;                                  catch ( Exception e )
                               customer.AccountLines.Add(                                 {
                                  string.Format( "Rental of book {0}.", book ),               if (ExceptionManager.Handle(e)) throw;
                                  book.RentalPrice );                                     }
                               customer.Balance -= book.RentalPrice;                  }
Business Code
+ Logging        + Transaktionen
+ Vorbedingungen + Exception Handling

+ Feature X
+ Feature Y
+ Feature Z
+…
Kern-            Seperation
funktionalitäten    of Concerns
  (Core Concerns)
VS
VS
    Nicht-
  Funktionale
 Anforderungen
     (Crosscutting Concerns)
Cross-Cutting Concerns


           Security             Data Binding
           Exception Handling   Thread Sync
           Tracing              Caching
           Monitoring           Validation
           Transaction          …
OOP



 OOP
+ AOP
Spring.NET
PostSharp    LinFu    Castle
                      MS Unity




Build-Time   Hybrid   Run-Time
Build-Time: “Statisch”          Run-Time: “Dynamisch”

Erfolgt bei Kompilierung        Erfolgt zur Laufzeit
Code wird direkt verändert      Code bleibt unverändert
Zur Laufzeit keine Änderungen   Zur Laufzeit Änderungen möglich
Auch auf Properties, Felder,    Aufruf wird über Proxy
Events anwendbar                umgeleitet
Keine Interfaces erforderlich   idR. Interfaces erforderlich (Proxy)
02
Live Coding
Logging
LogTimeAspect




          webnoteaop.codeplex.com
Exceptions
ConvertExceptionAspect




                         webnoteaop.codeplex.com
Validierung
ValidationGuardAspect




                        webnoteaop.codeplex.com
Caching
SimpleCacheAspect




           webnoteaop.codeplex.com
03
AOP 1 x 1
AspectJ Begriffe




                   Join Point
                   Pointcut
                   Advice
                   Aspect
AspectJ Begriffe




                   Join Point
                   Pointcut
                   Advice
                   Aspect
IL Code Vorher




         [LogTimeAspect]
         public ActionResult Index()
         {
             IEnumerable<NoteWithCategories> notes =
                 this.WebNoteService.ReadAll();
             return View(notes);
         }
IL Code Nachher

   public ActionResult Index()
   {
       ActionResult CS$1$2__returnValue;
       MethodExecutionArgs CS$0$3__aspectArgs =
           new MethodExecutionArgs(null, null);
       <>z__Aspects.a68.OnEntry(CS$0$3__aspectArgs);
       try
       {
           IEnumerable<NoteWithCategories> notes =
               this.WebNoteService.ReadAll();
           ActionResult CS$1$0000 = base.View(notes);
           CS$1$2__returnValue = CS$1$0000;
       }
       finally
       {
           <>z__Aspects.a68.OnExit(CS$0$3__aspectArgs);
       }
       return CS$1$2__returnValue;
   }
Originale Methode       Aspekt Klasse

                            OnEntry
  try
  {
        Method Body
                            OnSuccess
  }
  catch (Exception e)
  {
                            OnException
  }
  finally
  {
                             OnExit
  }

                        : OnMethodBoundaryAspect
04
Installation
www.sharpcrafters.com/postsharp/download
nuget
http://nuget.org/packages/PostSharp
Spring.NET            PostSharp
springframework.net   sharpcrafters.com


Castle                Demo Download
castleproject.org     webnoteaop.codeplex.com


Unity
unity.codeplex.com
FRAGEN?
Bis bald
›   10.05.2012 – .NET UG Karlsruhe: NoSQL
›   14.05.2012 – .NET Developer Conference (DDC)
                 .Nürnberg: NoSQL
Vielen Dank!
Primitive Aspekt-Typen


›   MethodBoundaryAspect       ›   LocationInterceptionAspect
    › OnEntry                       › OnGetValue
    › OnSuccess                     › OnSetValue
    › OnException
    › OnExit                   ›   EventInterceptionAspect
                                    › OnAddHandler
›   OnExceptionAspect               › OnRemoveHandler
     › OnException                  › OnInvokeHandler

›   MethodInterceptionAspect   ›   MethodImplementationAspect
    › OnInvoke                     › OnInvoke

                               ›   CompositionAspect
                                    › CreateImplementationObject
Bildnachweise
Ausgewählter Ordner © Spectral-Design – Fotolia.com
Warnhinweis-Schild © Sascha Tiebel – Fotolia.com
Liste abhaken © Dirk Schumann – Fotolia.com
3D rendering of an architecture model 2 © Franck Boston – Fotolia.com
Healthcare © ArtmannWitte – Fotolia.com
Stressed businessman © Selecstock – Fotolia.com
Funny cartoon boss © artenot – Fotolia.com

2012-04-12 - AOP .NET UserGroup Niederrhein

  • 1.
  • 2.
    Johannes Hoppe ASP.NET MVCWebentwickler www.johanneshoppe.de
  • 3.
  • 4.
  • 5.
    Business Code public classCustomerProcesses { public void RentBook( int bookId, int customerId ) { Book book = Book.GetById( bookId ); Customer customer = Customer.GetById( customerId ); book.RentedTo = customer; customer.AccountLines.Add( string.Format( "Rental of book {0}.", book ), book.RentalPrice ); customer.Balance -= book.RentalPrice; } }
  • 6.
  • 8.
    Business Code public classCustomerProcesses { public void RentBook( int bookId, int customerId ) { Book book = Book.GetById( bookId ); Customer customer = Customer.GetById( customerId ); book.RentedTo = customer; customer.AccountLines.Add( string.Format( "Rental of book {0}.", book ), book.RentalPrice ); customer.Balance -= book.RentalPrice; } }
  • 9.
    Business Code + Logging internal class CustomerProcesses { private static readonly TraceSource trace = new TraceSource( typeof (CustomerProcesses).FullName ); public void RentBook( int bookId, int customerId ) { trace.TraceInformation( "Entering CustomerProcesses.CreateCustomer( bookId = {0}, customerId = {1} )", bookId, customerId ); try { Book book = Book.GetById( bookId ); Customer customer = Customer.GetById( customerId ); book.RentedTo = customer; customer.AccountLines.Add( string.Format( "Rental of book {0}.", book ), book.RentalPrice ); customer.Balance -= book.RentalPrice; trace.TraceInformation( "Leaving CustomerProcesses.CreateCustomer( bookId = {0}, customerId = {1} )", bookId, customerId ); } catch ( Exception e ) { trace.TraceEvent( TraceEventType.Error, 0, "Exception: CustomerProcesses.CreateCustomer( bookId = {0}, customerId = {1} ) failed : {2}", bookId, customerId, e.Message ); throw; } } }
  • 10.
    Business Code + Logging internal class CustomerProcesses + Vorbedingungen { private static readonly TraceSource trace = new TraceSource(typeof(CustomerProcesses).FullName); public void RentBook(int bookId, int customerId) { if (bookId <= 0) throw new ArgumentOutOfRangeException("bookId"); if (customerId <= 0) throw new ArgumentOutOfRangeException("customerId"); trace.TraceInformation( "Entering CustomerProcesses.CreateCustomer( bookId = {0}, customerId = {1} )", bookId, customerId); try { Book book = Book.GetById(bookId); Customer customer = Customer.GetById(customerId); book.RentedTo = customer; customer.AccountLines.Add(string.Format("Rental of book {0}.", book), book.RentalPrice); customer.Balance -= book.RentalPrice; trace.TraceInformation( "Leaving CustomerProcesses.CreateCustomer( bookId = {0}, customerId = {1} )“, bookId, customerId); } catch (Exception e) { trace.TraceEvent(TraceEventType.Error, 0, "Exception: CustomerProcesses.CreateCustomer( bookId = {0}, customerId = {1} ) failed : {2}", bookId, customerId, e.Message); throw; } } }
  • 11.
    Business Code + Logging + Transaktionen + Vorbedingungen internal class CustomerProcesses ts.Complete(); { } private static readonly TraceSource trace = new TraceSource(typeof(CustomerProcesses).FullName); break; } public void RentBook(int bookId, int customerId) catch (TransactionConflictException) { { if (bookId <= 0) if (i < 3) throw new ArgumentOutOfRangeException("bookId"); continue; if (customerId <= 0) else throw new ArgumentOutOfRangeException("customerId"); throw; } trace.TraceInformation( } "Entering CustomerProcesses.CreateCustomer( bookId = {0}, customerId = {1} )“, bookId, customerId); trace.TraceInformation( "Leaving CustomerProcesses.CreateCustomer( try bookId = {0}, customerId = {1} )", { bookId, customerId); for (int i = 0; ; i++) } { catch (Exception e) try { { trace.TraceEvent(TraceEventType.Error, 0, using (var ts = new TransactionScope()) "Exception: CustomerProcesses.CreateCustomer( bookId = {0}, { customerId = {1} ) failed : {2}", Book book = Book.GetById(bookId); bookId, customerId, e.Message); Customer customer = throw; Customer.GetById(customerId); } } book.RentedTo = customer; customer.AccountLines.Add( } string.Format("Rental of book {0}.", book), book.RentalPrice); customer.Balance -= book.RentalPrice;
  • 12.
    Business Code + Logging + Transaktionen + Vorbedingungen + Exception Handling internal class CustomerProcesses { ts.Complete(); private static readonly TraceSource trace = } new TraceSource(typeof(CustomerProcesses).FullName); break; public void RentBook(int bookId, int customerId) } { catch ( TransactionConflictException ) if (bookId <= 0) throw new ArgumentOutOfRangeException("bookId"); { if (customerId <= 0) if ( i < 3 ) throw new ArgumentOutOfRangeException("customerId"); continue; else try throw; { } trace.TraceInformation( } "Entering CustomerProcesses.CreateCustomer( bookId = {0}, customerId = {1} )", trace.TraceInformation( bookId, customerId ); "Leaving CustomerProcesses.CreateCustomer( bookId = {0}, customerId = {1} )", try bookId, customerId ); { } for ( int i = 0;; i++ ) catch ( Exception e ) { { try trace.TraceEvent( TraceEventType.Error, 0, { "Exception: CustomerProcesses.CreateCustomer( using ( var ts = new TransactionScope() ) bookId = {0}, customerId = {1} ) failed : {2}", { bookId, customerId, e.Message ); Book book = Book.GetById( bookId ); throw; Customer customer = Customer.GetById( customerId ); } } book.RentedTo = customer; catch ( Exception e ) customer.AccountLines.Add( { string.Format( "Rental of book {0}.", book ), if (ExceptionManager.Handle(e)) throw; book.RentalPrice ); } customer.Balance -= book.RentalPrice; }
  • 13.
    Business Code + Logging + Transaktionen + Vorbedingungen + Exception Handling + Feature X + Feature Y + Feature Z +…
  • 14.
    Kern- Seperation funktionalitäten of Concerns (Core Concerns)
  • 15.
  • 16.
    VS Nicht- Funktionale Anforderungen (Crosscutting Concerns)
  • 17.
    Cross-Cutting Concerns Security Data Binding Exception Handling Thread Sync Tracing Caching Monitoring Validation Transaction …
  • 18.
  • 19.
    Spring.NET PostSharp LinFu Castle MS Unity Build-Time Hybrid Run-Time
  • 20.
    Build-Time: “Statisch” Run-Time: “Dynamisch” Erfolgt bei Kompilierung Erfolgt zur Laufzeit Code wird direkt verändert Code bleibt unverändert Zur Laufzeit keine Änderungen Zur Laufzeit Änderungen möglich Auch auf Properties, Felder, Aufruf wird über Proxy Events anwendbar umgeleitet Keine Interfaces erforderlich idR. Interfaces erforderlich (Proxy)
  • 21.
  • 23.
    Logging LogTimeAspect webnoteaop.codeplex.com
  • 24.
    Exceptions ConvertExceptionAspect webnoteaop.codeplex.com
  • 25.
    Validierung ValidationGuardAspect webnoteaop.codeplex.com
  • 26.
    Caching SimpleCacheAspect webnoteaop.codeplex.com
  • 28.
  • 29.
    AspectJ Begriffe Join Point Pointcut Advice Aspect
  • 30.
    AspectJ Begriffe Join Point Pointcut Advice Aspect
  • 31.
    IL Code Vorher [LogTimeAspect] public ActionResult Index() { IEnumerable<NoteWithCategories> notes = this.WebNoteService.ReadAll(); return View(notes); }
  • 32.
    IL Code Nachher public ActionResult Index() { ActionResult CS$1$2__returnValue; MethodExecutionArgs CS$0$3__aspectArgs = new MethodExecutionArgs(null, null); <>z__Aspects.a68.OnEntry(CS$0$3__aspectArgs); try { IEnumerable<NoteWithCategories> notes = this.WebNoteService.ReadAll(); ActionResult CS$1$0000 = base.View(notes); CS$1$2__returnValue = CS$1$0000; } finally { <>z__Aspects.a68.OnExit(CS$0$3__aspectArgs); } return CS$1$2__returnValue; }
  • 33.
    Originale Methode Aspekt Klasse OnEntry try { Method Body OnSuccess } catch (Exception e) { OnException } finally { OnExit } : OnMethodBoundaryAspect
  • 34.
  • 35.
  • 36.
  • 37.
    Spring.NET PostSharp springframework.net sharpcrafters.com Castle Demo Download castleproject.org webnoteaop.codeplex.com Unity unity.codeplex.com
  • 38.
  • 39.
    Bis bald › 10.05.2012 – .NET UG Karlsruhe: NoSQL › 14.05.2012 – .NET Developer Conference (DDC) .Nürnberg: NoSQL
  • 40.
  • 41.
    Primitive Aspekt-Typen › MethodBoundaryAspect › LocationInterceptionAspect › OnEntry › OnGetValue › OnSuccess › OnSetValue › OnException › OnExit › EventInterceptionAspect › OnAddHandler › OnExceptionAspect › OnRemoveHandler › OnException › OnInvokeHandler › MethodInterceptionAspect › MethodImplementationAspect › OnInvoke › OnInvoke › CompositionAspect › CreateImplementationObject
  • 42.
    Bildnachweise Ausgewählter Ordner ©Spectral-Design – Fotolia.com Warnhinweis-Schild © Sascha Tiebel – Fotolia.com Liste abhaken © Dirk Schumann – Fotolia.com 3D rendering of an architecture model 2 © Franck Boston – Fotolia.com Healthcare © ArtmannWitte – Fotolia.com Stressed businessman © Selecstock – Fotolia.com Funny cartoon boss © artenot – Fotolia.com