mercoledì 31 agosto 2011

Tecniche avanzate di AOP


Nel mio precedente articolo Cancella trasversali preoccupazioni con la programmazione Aspect Oriented in. NET , ho trattato le basi di Aspect Oriented Programming (AOP). Da allora ho ricevuto un feedback chiedendo di nuovi modi per utilizzare AOP per le operazioni più avanzate come la sicurezza e la filettatura, e riguarderà quelle qui. Devo dire anche andare oltre il modo per applicare le aspetti personalizzato a un'applicazione di esempio completo.
Sicurezza movimentazione
Prima diamo un'occhiata a come gestire l'autenticazione e autorizzazione con aspetti. Il nostro aspetto autenticazione verificherà se l'utente è autenticato. Se l'utente non è autenticato, ci evita che il metodo di esecuzione con un'eccezione di utenti non autorizzati.

Le intercettazioni AuthenticatedAttribute aspetto il punto di ingresso del metodo applicato (s) e genera un UnauthorizedAccessException se l'utente corrente non è autenticato. Noi dichiariamo che la nostra attributo fornirà un aspetto della sicurezza. Abbiamo anche creato alcune dipendenze per garantire che il nostro aspetto sarà applicato prima che tutti gli aspetti Threading o autorizzazione. Queste dipendenze sarà utile in seguito quando cominciare ad applicare più attributi ad un metodo di destinazione.
Iniziate aggiungendo seguenti spazi dei nomi al file:
utilizzando System.Threading; 
utilizzando PostSharp.Aspects, 
utilizzando PostSharp.Aspects.Dependencies;
[Serializable] [ProvideAspectRole (StandardRoles.Security)] [AspectRoleDependency (AspectDependencyAction.Order, AspectDependencyPosition.Before, StandardRoles.Threading)] [AspectRoleDependency (AspectDependencyAction.Order, AspectDependencyPosition.Before, "Autorizzazione")] public class AuthenticatedAttribute: OnMethodBoundaryAspect { pubblico OnEntry override void (MethodExecutionArgs args) { if (Thread.CurrentPrincipal.Identity.IsAuthenticated!) { throw new UnauthorizedAccessException ("Tu non sei un utente valido."); } } }
L'aspetto AuthorizationAttribute è molto simile nella sua attuazione. Noi intercettare il punto di ingresso del metodo applicato (s) e verificare se l'utente è nei ruoli dato passato all'attributo. Se l'utente non è in tutti i ruoli dato, viene lanciato un UnauthorizedAccessException. Il nostro AuthorizationAttribute è dichiarato di fornire un aspetto ruolo di autorizzazione.Questo ruolo aspetto personalizzato è utilizzato dal aspetto di autenticazione per garantire gli aspetti ruolo di autorizzazione vengono eseguiti prima di aspetti ruolo alcuna autorizzazione.
Assicurarsi di aggiungere spazi dei nomi seguenti:
utilizzando System.Threading; 
utilizzando System.Security.Principal, 
utilizzando PostSharp.Aspects, 
utilizzando PostSharp.Aspects.Dependencies;
[Serializable] [ProvideAspectRole ("Autorizzazione")] public class AuthorizedAttribute: OnMethodBoundaryAspect { private string _roles = string.Empty;
     AuthorizedAttribute pubblico (ruoli stringa) { _roles = ruoli; }
     public override OnEntry void (MethodExecutionArgs args) { if (Thread.CurrentPrincipal.IsInRole (_roles)!) { throw new UnauthorizedAccessException ("Non sei autorizzato ad utilizzare questa applicazione."); } } }
Manipolazione Threading
Al di là della sicurezza, AOP è anche adatto per la gestione di thread all'interno di un'applicazione. Un problema comune WPF è l'incapacità di aggiornare il thread dell'interfaccia utente da un altro thread. Non sarebbe fantastico se si potesse semplicemente decorare il metodo che aggiorna l'elemento dell'interfaccia utente (s) con un attributo, invece di duplicare la logica di spedizione in tutto il mondo? Non temere, PostSharp ci permette di incapsulare facilmente questo spesso ripetuta compito!
Per implementare il nostro aspetto personalizzato RunOnUIThreadAttribute, utilizziamo la classe MethodInterceptionAspect base per intercettare la chiamata di metodo. Invece che direttamente l'esecuzione del metodo, si controlla per vedere se siamo già sul thread dell'interfaccia utente attraverso il metodo CheckAccess del DispatcherObject. Se siamo, abbiamo semplicemente chiamare il metodo, come al solito, se siamo in un altro thread, spediamo la chiamata al metodo al thread dell'interfaccia utente attraverso il Dispatcher WPF. Come i nostri aspetti di sicurezza personalizzate, facciamo in modo di dichiarare un ruolo di fornitore di aspetto, che può essere utilizzato per risolvere i conflitti tra aspetti. Entrambi i nostri aspetti threading vengono dichiarati con il ruolo aspetto StandardRoles.Threading.
Avrete bisogno di importare gli spazi dei nomi seguenti:
utilizzando System.Threading; 
utilizzando PostSharp.Aspects, 
utilizzando PostSharp.Aspects.Dependencies;
[Serializable] [ProvideAspectRole (StandardRoles.Threading)] RunOnUIThreadAttribute public class: MethodInterceptionAspect { pubblico OnInvoke override void (MethodInterceptionArgs args) { DispatcherObject dispatchedObj = (DispatcherObject) args.Instance;
        if (dispatchedObj.CheckAccess ()) { args.Proceed (); } else { dispatchedObj.Dispatcher.Invoke ((Azione) (() => args.Proceed ())); } } }
Ci sono molte volte ti consigliamo di eseguire un compito particolare su un thread in background. Un buon esempio è l'esecuzione di un compito resource-intensive come ad esempio una query di database o di servizio richiesta web. Per gestire questi casi, possiamo utilizzare un aspetto personalizzato che eseguirà qualsiasi metodo a cui viene applicato il pool di thread. Faremo uso della classe base MethodIncerceptionAspect ancora una volta ad intercettare punto il metodo di destinazione di invocazione alla coda il metodo chiamata al pool di thread.
Avrete bisogno di importare gli spazi dei nomi seguenti:
utilizzando System.Threading; 
utilizzando System.Windows.Threading, 
utilizzando PostSharp.Aspects, 
utilizzando PostSharp.Aspects.Dependencies;

ou'll also need add a reference to WindowsBase.
[Serializable] [ProvideAspectRole(StandardRoles.Threading)] public class RunOnThreadPoolAttribute : MethodInterceptionAspect { public override void OnInvoke(MethodInterceptionArgs args) { ThreadPool.QueueUserWorkItem(methodState => args.Proceed()); } }

Avrete bisogno anche di aggiungere un riferimento a WindowsBase.
[Serializable] [ProvideAspectRole (StandardRoles.Threading)] RunOnThreadPoolAttribute public class: MethodInterceptionAspect { pubblico OnInvoke override void (MethodInterceptionArgs args) { ThreadPool.QueueUserWorkItem (methodState => args.Proceed ()); } }
Aspetti applicando in un'applicazione di esempio
Ora che abbiamo creato il nostro aspetti potenza necessaria per gestire problemi di sicurezza e filettatura, cerchiamo di metterli a frutto. La nostra applicazione di esempio è costituito da due forme: di login e di ricerca. Il modulo di ricerca sarà accessibile solo a un utente autenticato nel ruolo di amministratore. Un utente autenticato otterrà la visualizzazione di un aggiornamento di stato "Collegato come x", dove x è il nome utente autenticato.

Iniziate creando un nuovo WPF C # 4.0 applicazione. Aggiungere una libreria di classi C # VSMAdvancedAOP.Aspects nome. Nell'ambito del progetto Aspetti aggiungere due cartelle - una sicurezza di nome e l'altro, Threading. Come mostrato nella Figura 1, aggiungere l'autenticazione e gli attributi di autorizzazione per la cartella di sicurezza e gli aspetti filettatura nella cartella Threading.

[Clicca sull'immagine per ingrandirla.]
Figura 1. Configurazione di un progetto
Configurare l'interfaccia utente
Quindi, aprire il file MainWindow.xaml nel progetto WPF e incollare il codice seguente nell'elemento <Grid> radice. Si dovrebbe essere simile a Figura 2 .
<StackPanel> Orientation="Horizontal"> <StackPanel <Label Nome utente MinHeight="15"> </ Label> <TextBox Name="txtUsername" MinWidth="150" MinHeight="15" /> </ StackPanel> <StackPanel Orientamento = "Orizzontale"> <Label MinHeight="15"> password </ label> <PasswordBox Name="txtPassword" MinWidth="150" MinHeight="15" /> </ StackPanel> Tasto <Name = Fare clic su "btnLogin" = "btnLogin_Click"> Login </ Button> <Button Name="btnSearch" Click="btnSearch_Click"> Cerca Admin </ Button> <Label Name="lblLoginStatus"> </ label> </ StackPanel>

[Clicca sull'immagine per ingrandirla.]
Figura 2. Il Login Form
Ora create una nuova finestra nel progetto WPF nome SearchWindow. Aprite SearchWindow.xaml e incollare il codice seguente nell'elemento <Grid> radice. Dovrebbe apparire come Figura 3 .
<StackPanel> <StackPanel Orientation="Horizontal"> <Label MinHeight="15"> ricerca </ label> <TextBox Name="txtSearch" MinWidth="150" MinHeight="15" /> </ StackPanel> <Pulsante Nome = Fare clic su "btnSearch" = "btnSearch_Click"> ricerca </ Button> <StackPanel Orientation="Horizontal"> <Label> Stato </ Label> <Label Name="lblStatus"> </ label> </ StackPanel> </ StackPanel >

[Clicca sull'immagine per ingrandirla.]
Figura 3. Il modulo di ricerca
Login Form
Ora che l'interfaccia utente è impostato per entrambi i nostri Login e forme di ricerca, cerchiamo di implementare il modulo di login. Aprite MainWindow.xaml.cs e aggiungere quanto segue per le importazioni namespace:
utilizzando System.Threading; 
utilizzando System.Security.Principal, 
utilizzando VSMAdvancedAOP.Aspects.Security, 
utilizzando VSMAdvancedAOP.Aspects.Threading;
Prossimo aggiungeremo il metodo Login che autentica l'utente.
Accedi pubblico bool (userName stringa, string password) { bool valido = false;
     Dizionario <string, string[]> validUsers = new Dictionary <string, string[]> (); validUsers ["utente"] = new string [] {"utente"}; validUsers ["admin"] = new string [] {"User", "Admin"};
     if (validUsers.ContainsKey (nome utente) & & username password ==) { Thread.CurrentPrincipal = nuovo GenericPrincipal (GenericIdentity nuovo ( userName, "Passport"), validUsers [username]); valido = true; }
     ritorno valido; }
Per autenticare l'utente, semplicemente fare una ricerca su un dizionario di utenti validi. Se l'utente esiste nel utenti valido ricerca, creare un GenericPrinical per il nome utente insieme con i suoi ruoli accessibile. In un sistema reale che molto probabilmente sarà l'autenticazione su un database, servizio Web o Active Directory. Ai fini della dimostrazione che ho scelto di creare un meccanismo di login molto semplice.
Successivamente, il cablaggio di pulsante Login Clicca per chiamare il metodo PerformLogin. Il metodo PerformLogin cancella l'etichetta di stato, autentica l'utente, e aggiorna l'etichetta di stato di accesso di un utente autenticato.
vuoto PerformLogin privato (username stringa, string password) { UpdateLoginStatus (string.Empty); if (Login (nome utente, password)) { UpdateLoginStatus (String.Format ("Collegato come {0}", nome utente)); } }
 private void UpdateLoginStatus (stato stringa) { lblLoginStatus.Content = stato; }
 private void btnLogin_Click (object sender, RoutedEventArgs e) { PerformLogin (txtUsername.Text, txtPassword.Password); }

Ora è il momento di legare il pulsante Admin Ricerca per visualizzare la finestra di ricerca per un utente autenticato e autorizzato attraverso l'uso dei nostri aspetti personalizzati. L'aspetto autenticati verrà eseguito per primo, seguito da l'aspetto autorizzato. L'aspetto autorizzato è passato un ruolo di "amministratore" per assicurare che l'utente è nel ruolo "Admin" degli utenti.
[Autenticata] [Autorizzato ("Admin")] private void btnSearch_Click (object sender, RoutedEventArgs e) { nuovo SearchWindow () Show ();. }
Modulo di ricerca
Ora che il modulo di login è completamente creato, implementiamo la finestra di ricerca. Aprite SearchWindow.xaml.cs e aggiungere le seguenti istruzioni utilizzando:
utilizzando System.Threading; 
utilizzando VSMAdvancedAOP.Aspects.Security, 
utilizzando VSMAdvancedAOP.Aspects.Threading;
Ora, aggiungere gli aspetti autenticato e autorizzato al costruttore SearchWindow. Questo farà sì che l'utente viene autenticato e nel ruolo utente Admin.
[Autenticata] [Autorizzato ("Admin")] SearchWindow pubblico () { InitializeComponent (); }
Quindi, aggiungere il metodo PerformSearch, che utilizza l'aspetto RunOnThreadPool. L'intero metodo verrà eseguito sul ThreadPool. Il metodo PerformSearch chiamato UpdatedSearchStatus imposta lo stato iniziale di ricerca per "Ricerca di x", dove x è il criterio di ricerca specificato. Una chiamata Thread.Sleep viene aggiunto per simulare una ricerca in corso per due secondi. Una volta che la ricerca viene eseguita il metodo UpdateSearchStatus è chiamato ad aggiornare lo stato di ricerca per "Fatto".
 [RunOnThreadPool] public void PerformSearch (criteri di stringa) { UpdateSearchStatus ("Ricerca ..."); Thread.Sleep (2000); UpdateSearchStatus ("Fatto"); }
Il metodo UpdateSearchStatus definisce semplicemente il testo dell'etichetta stato di ricerca. Il metodo è decorato con l'aspetto RunOnUIThread per assicurarsi che sia eseguita sul thread dell'interfaccia utente.
 [RunOnUIThread] public void UpdateSearchStatus (stato stringa) { lblStatus.Content = stato; }
Infine, impostare l'evento Click di ricerca per chiamare il metodo PerformSearch e passare il valore di ricerca. I moduli compilati dovrebbe essere simile a quelli in Figura 4 .
 private void btnSearch_Click (object sender, RoutedEventArgs e) { PerformSearch (txtSearch.Text); }

[Clicca sull'immagine per ingrandirla.]
Figura 4. L'interfaccia utente del programma completato.
Vantaggi AOP
AOP ha molte applicazioni pratiche per la gestione dei problemi trasversali, come la registrazione, la sicurezza e la filettatura. Utilizzando le tecniche AOP consente non solo di ridurre l'ingombro codice, ma aumentare la facilità di manutenzione della propria applicazione da parte degli sviluppatori, sia esperti e nuovi.