2015-11-21 17 views
6

Per qualche strano motivo, entrambe le classi System.Runtime.Remoting.Messaging.CallContext e AsyncLocal sono disponibili solo utilizzando l'intero CLR. Ciò rende molto difficile fare scoping asincrono mentre si lavora con le librerie di classi portatili o le applicazioni di Windows Phone, soprattutto dal momento che le API di Windows Phone stanno diventando solo asincrone; quindi non abbiamo davvero un'opzione per non usare async/await.Implementazione dell'oscillazione asincrona nelle applicazioni PCL e Windows Phone

Ciò significa in pratica è che in WPF e WinForms, possiamo scrivere metodi come questo:

private async void button_Click(object sender, EventArgs e) 
{ 
    CallContext.LogicalSetData("x", new object()); 
    await Something(); 
    var context = CallContext.LogicalGetData("x"); 
} 

In WPF e WinForms, il quadro assicura che ogni clic allo stesso pulsante ottenere il loro proprio contesto e con che può funzionare in isolamento. È difficile ottenere lo stesso utilizzando ThreadLocal<T> e [ThreadStatic] poiché ogni clic verrà eseguito sul thread dell'interfaccia utente.

La mia domanda è, come risolveremmo questo problema in Windows Phone, Store e altri tipi di applicazioni che non supportano CallContext e AsyncLocal<T>?

Alcune informazioni di base:

Molto spesso ci vogliono (business) della logica per l'esecuzione in una sorta di contesto. Questo contesto può contenere informazioni che la logica aziendale può utilizzare durante l'operazione. In un ambiente server questo è davvero facile da immaginare, perché è necessario eseguire richieste in una transazione di database, è necessario accedere all'utente corrente, id titolare, ecc. Ma anche in un'applicazione client, le operazioni potrebbero dover accedere a informazioni contestuali come un ID di correlazione per l'operazione corrente in esecuzione o contesto per la registrazione. Durante tale operazione (come un evento click) potremmo aver bisogno di risolvere servizi aggiuntivi (dal nostro Composition Root). Affinché l'operazione funzioni correttamente, potrebbe essere necessario riutilizzare lo stesso componente nell'intera operazione del client e ciò significa che il nostro Root di composizione deve essere consapevole del contesto in cui è in esecuzione.

Sebbene tutte queste informazioni possano essere trasmesse attraverso l'intero sistema utilizzando le chiamate di metodo dell'API pubblica dei servizi, ciò non solo ci costringerebbe a inquinare l'API dei servizi nella nostra applicazione con dettagli di implementazione, ma porterebbe a gravi problemi di manutenzione, perché dovremmo passare attraverso tutte queste informazioni in tutto il sistema, e una semplice modifica interna a uno dei nostri componenti si propagherebbe allo stack delle chiamate attraverso tutte le chiamate dei metodi. E quando si tratta del nostro Root di composizione, non vogliamo assolutamente passare un oggetto cache/scope della nostra libreria DI attraverso l'applicazione, perché ciò accoppiasse strettamente la nostra logica aziendale a una libreria esterna. Ovviamente, non vogliamo nemmeno trasmettere una sorta di service locator.

Quindi l'implementazione dell'ambito utilizzando qualcosa come CallContext o AsyncLocal<T> è molto importante nelle applicazioni client.

risposta

3

Non esiste una soluzione facile per questo oggi, mi dispiace. Quando Windows Phone (eventualmente) diventa "Windows 10 su un telefono" (ad esempio, con AsyncLocal<T>), ciò sarà possibile. Ma per ora ...

Il modo più semplice per farlo è passare il contesto, esplicitamente (come parametro) o implicitamente (come variabile membro su this).

Potrebbe anche essere possibile ottenere una versione limitata di questo con i personalizzatori. Ma oltre ad essere orribilmente complessa, questa soluzione richiederebbe di modificare ogni await nella tua intera app (o usare una riscrittura IL post-compilazione per ottenere lo stesso effetto).

+2

Crap. Non è la risposta che speravo. – Steven

+0

@Steven: Siamo spiacenti! Come premio di consolazione, sei libero di credere che i contesti impliciti non siano un grande progetto. Sono utili per il contesto di registrazione (raggruppando più messaggi di registro in un'unica "attività" logica), e questo è tutto. I contesti invisibili sono un meccanismo inadeguato per una vera logica/dati aziendali: rendono il sistema difficile da testare e mantenere. Scrivo tonnellate di codice asincrono e (eccetto per la correlazione "attività"), utilizzo solo contesti impliciti per creare stack di causalità durante il debugging, mai in produzione e certamente non portando mai informazioni di livello aziendale. –

+0

Sono d'accordo che non va bene per la logica di business, ma contesti trasparenti (come il TransactionScope) sono grandi per le preoccupazioni infrastrutturali e in realtà rendono molto più facile la vita degli sviluppatori. [Simple Injector] (https://simpleinjector.org) (un lib di DI) ha basato il suo design di stili di vita attorno a questo concetto e questo rende la vita dello sviluppatore molto più facile, perché permette alla logica di business di essere completamente ignare dello scopo è in esecuzione. Passare attraverso l'ambito del contenitore DI in tutto il sistema porta a un forte accoppiamento sul contenitore DI, che è una cattiva pratica. – Steven

Problemi correlati