2013-01-13 8 views
17

Eventuali duplicati:
C# wrap method via attributesimplementazione di base di AOP come attributo utilizzando standard di .NET Framework

mi piacerebbe ottenere tale funzionalità:

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

Dove [Atomic] attributo è un attributo che racchiude la logica della funzione nell'ambito di una transazione:

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

Come scrivere un attributo simile?

Ho chiesto prima sostanzialmente lo stesso question, so che questo può essere fatto utilizzando AOP, ma non ho menzionato sto cercando qualche semplice prova di implementazione del concetto o articoli utili che possono aiutarmi a scrivere questo utilizzando puro .NET Framework (suppongo che utilizzi i tipi RealProxy e MarshalByRefObject, di cui ho letto le domande relative alla navigazione).

Ho bisogno di risolvere esattamente questo esempio mostrato. Sembra una cosa fondamentale quindi voglio imparare come farlo partendo da zero. Per ora non ha bisogno di essere sicuro e flessibile.

+0

Se stai facendo questo per la tua istruzione, va bene, ma se stai facendo questo per risolvere un requisito aziendale, allora è già stato fatto per te in WCF e COM +. –

risposta

20

Sembra una cosa fondamentale ...

E 'una delle (tante) cose che sono semplici da capire il concetto, ma non è affatto semplice da implementare.

Come da Oded's answer, Attributi in .NET non fare nulla. Esiste solo in modo che altri codici (o sviluppatori) possano guardarli in seguito. Pensalo come un commento di fantasia.

Con questo in mente, è possibile scrivere l'attributo come questo

public class AtomicAttribute : Attribute { } 

Ora la parte più difficile, è necessario scrivere del codice per la ricerca di quell'attributo, e modificare il comportamento del codice.

Dato che C# è un linguaggio compilato, e date le regole del CLR .NET ci sono teoricamente 3 modi per fare questo Hook

  1. nel compilatore C#, e lo rendono diverso codice di uscita quando si vede quell'attributo.
    Questo sembra che sarebbe bello, ma semplicemente non è possibile in questo momento. Forse il progetto potrebbe consentirlo in futuro, ma per ora non è possibile farlo.

  2. Scrivi qualcosa, che esplorerà l'assembly .NET dopo il # compilatore C ha convertito a MSIL, e cambiare il MSIL.
    Questo è fondamentalmente ciò che fa PostSharp. Scansione e riscrittura di MSIL è difficile. Esistono librerie come Mono.Cecil che possono essere d'aiuto, ma è comunque un problema estremamente difficile. Può anche interferire con il debugger, ecc.

  3. Utilizzare le API di .NET Profiling per monitorare il programma mentre è in esecuzione e ogni volta che viene visualizzata una chiamata di funzione con tale attributo, reindirizzare a un'altra funzione di wrapper.
    Questa è forse l'opzione più semplice (anche se è ancora molto difficile), ma lo svantaggio è che il tuo programma ora deve essere eseguito con il profiler. Questo può andare bene sul tuo PC di sviluppo, ma causerà un grosso problema se proverai a distribuirlo. Inoltre, è probabile che ci sia un grande successo nelle prestazioni usando questo approccio.

A mio avviso, la soluzione migliore è quello di creare una funzione wrapper che imposta la transazione, e poi passarlo una lambda, che fa il lavoro vero e proprio. Come questo:

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 
    } 
} 

Il termine di fantasia informatica di questo è Higher order programming

+0

Grazie per il tuo tempo. Sto iniziando a ottenere ciò che in realtà ho davvero chiesto. Saluti. – jwaliszko

+0

Non è esattamente vero che gli Attributi non fanno nulla. Guarda System.Web.Http.AuthorizeAttribute che gestisce l'autorizzazione dell'IPrincipal di una richiesta. – Polymorphix

+2

@Polymorphix Nope, anche System.Web.Http.AuthorizeAttribute non fa nulla. Quando l'API Web ASP.NET sta eseguendo il framework e creando istanze delle classi, cerca AuthorizeAttribute e agisce in modo diverso se lo vede, ma lo stesso attributo si trova lì in attesa che qualcuno lo cerchi –

10

Gli attributi sono metadati, ovvero tutti sono.

Esistono molti strumenti che possono trarre vantaggio da tali metadati, ma tali strumenti devono essere consapevoli dell'attributo.

Gli strumenti AOP come PostSharp leggono tali metadati per sapere cosa e dove intrecciare gli aspetti nel codice.

In breve - solo scrivendo un AtomicAttribute vi darà nulla - è necessario passare l'assembly compilato attraverso uno strumento che sa di questo attributo e fare "qualcosa" ad esso al fine di raggiungere AOP.

3

Non è affatto una cosa di base. Nessun codice extra viene eseguito solo perché un metodo ha un attributo, quindi non c'è un posto dove mettere il tuo codice TransactionScope.

Quello che dovresti fare è all'avvio dell'applicazione utilizzare la riflessione per scorrere su ogni metodo su ogni classe nel tuo assieme e trovare i metodi che sono contrassegnati con AtomicAttribute, quindi scrivere un proxy personalizzato attorno a quell'oggetto. Quindi in qualche modo ottenere tutto il resto per chiamare il proxy invece dell'implementazione reale, magari utilizzando un framework di iniezione delle dipendenze.

La maggior parte dei framework AOP esegue questa operazione in fase di compilazione. PostSharp, ad esempio, viene eseguito dopo che VisualStudio ha creato l'assembly. Analizza il tuo assembly e riscrive il codice IL per includere i proxy e gli intercettori AOP. In questo modo l'assembly è pronto per l'esecuzione quando viene eseguito, ma l'IL è cambiato rispetto a quello che hai scritto in origine.

+0

Mi piacerebbe entrare in quest'area con alcuni dettagli per estendere la mia percezione del Framework e fare alcune cose di base che hai menzionato nel secondo paragrafo. Grazie per la risposta. – jwaliszko

2

Forse risolvere tutti gli oggetti utilizzando contenitore CIO? Puoi configurare gli intercettori per i tuoi tipi e in essi controllare se il metodo chiamato è decorato con quell'attributo. È possibile memorizzare tali informazioni in modo da non dover utilizzare la riflessione su ogni chiamata di metodo.

Quindi, quando si esegue questa operazione:

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

something non è oggetto è stato implementato, ma proxy. In quel proxy puoi fare quello che vuoi prima e dopo la chiamata al metodo.

Problemi correlati