2009-05-06 24 views
7

Sto utilizzando il modello Model-View-Presenter in un progetto WinForms e un problema (tra molti) che sto avendo è quando il modulo dice al presentatore di fare qualcosa e poi è non reattivo mentre il presentatore passa a farlo. Fortunatamente nel mio progetto non ho alcun problema a rendere tutte le chiamate del relatore asincrone la domanda è come esattamente farlo?Best practice per le chiamate asincrone in MVP con WinForms

caso ogni chiamata presentatore solo essere avvolto in una nuova creazione discussione? *

new Thread(()=>_presenter.DoSomething()).Start(); 

Quali sono le migliori pratiche qui? Cosa succede se l'utente preme un pulsante "Annulla quello che stai facendo"? Come posso abortire con garbo?

. * Realisticamente avrei probabilmente solo utilizzare una sorta di un proxy sul presentatore di fare questo piuttosto che mettere la creazione thread nel WinForm

+0

Sorpreso di non vedere una vera partecipazione qui. Sarei stato interessato anche a questo. – Houman

risposta

2

Posso solo affermare che ho pensato a questo (prima di leggere la tua domanda;). Per prima cosa vorrei sistemare i posti in cui ciò è veramente importante; per esempio il chokepoint di accesso al DB. Se esiste un luogo che non deve essere eseguito nel contesto "UI" (è possibile salvarlo da http://msdn.microsoft.com/en-us/library/system.threading.synchronizationcontext.current.aspx nel thread dell'interfaccia utente e quindi confrontarlo in seguito con il contesto di sincronizzazione non dell'interfaccia utente), quindi Debug.BitchAndMoan(). Qualunque calcolo più lungo (che "dovrebbe" essere chiaramente separato nelle proprie varietà, giusto;) dovrebbe affermarlo.

Immagino che dovresti almeno rendere configurabile il tipo di esecuzione della funzione di presentatore tramite l'attributo che viene poi obbedito dal proxy. (nel caso in cui si desideri eseguire qualcosa in modo seriale).

L'annullamento di un'attività è in realtà un problema del presentatore, ma è necessario disporre di un oggetto di riferimento che indichi cosa si desidera interrompere. Se si utilizza la modalità proxy, è possibile raccogliere i thread creati nell'elenco delle attività con IAsyncResult, ma è ancora un problema decidere quale si suppone venga annullato se è possibile chiamare più volte la stessa azione in parallelo. Quindi è necessario fornire l'attività con un nome specifico di chiamata appropriato quando lo si avvia; che implica troppa logica in Vista lato -> Presenter dovrebbe probabilmente chiedere a View di chiedere all'utente quale delle attività deve essere smaltita.

La mia esperienza è che questo di solito viene semplicemente aggirato usando eventi (stile SCSF). Se lo facessi da zero, andrei in modo proxy dato che SCSF è stato un problema in così tanti modi che dubito della sanità mentale dei suoi progettisti.

+0

Non troppi problemi con i chokepoint nella mia applicazione, solo 5 o 6 luoghi diversi in cui un'interazione utente viene instradata a un presentatore e tutti possono permettersi di essere asincroni. Che ne dici di girare effettivamente il nuovo thread? nuova discussione? BackgroundWorker? Qualcos'altro? –

+1

I _think_ BW è inteso più per un'attività nota che fa sempre la stessa cosa, quindi il thread di spawning dovrebbe essere di tipo idiomatic .NET qui. A seconda di ciò che si fa, si consiglia di utilizzare threadpool: http://msdn.microsoft.com/en-us/library/ms973903.aspx. Per impostazione predefinita, il threadpool ha una profondità di 25, quindi è possibile esaminare/verificare che (questo limite non si applica se si genera sempre un nuovo thread) –

0

Perché non rendere il modello di proxy si utilizza accettare un paio di chiamata spalle restituire i risultati o interrompere?

+0

Mi dispiace, ma questo in nessun modo risponde alla mia domanda. Mi sto chiedendo quali sono le migliori pratiche per eseguire chiamate in modo asincrono? Nuova discussione? BackgroundWorker? Come fare specificamente una interruzione? Non ho bisogno di preoccuparmi dei callback, questo è l'MVP tradizionale, quindi il presentatore non restituisce mai nulla. –

4

Io di solito si svolge ogni azione che può (realisticamente) prendere più di un secondo o due in un compito a parte, qualcosa di simile a:

public interface ITask 
{ 
    void ExecuteTask (ITaskExecutionContext context); 
    void AfterSuccess(ITaskExecutionContext context); 
    void AfterFailure(ITaskExecutionContext context); 
    void AfterAbortion(ITaskExecutionContext context); 
} 

ho anche avere un'astrazione per l'esecuzione di tali compiti:

public interface ITaskExecutor : IDisposable 
{ 
    void BeginTask(ITask task); 
    void TellTaskToStop(); 
} 

una delle implementazioni di questo ITaskExecutor sta usando il BackgroundWorker:

public class BackgroundTaskExecutor : ITaskExecutor 
{ 
    public void BeginTask(ITask task) 
    { 
     this.task = task; 
     worker = new BackgroundWorker(); 
     worker.DoWork += WorkerDoWork; 
     worker.RunWorkerCompleted += WorkerRunWorkerCompleted; 
     worker.WorkerSupportsCancellation = true; 

     worker.RunWorkerAsync(); 
    } 

    ... 
} 

Faccio molto affidamento sull'iniezione delle dipendenze e sull'IoC per collegare le cose insieme. Nel presentatore poi mi basta chiamare qualcosa come:

GoAndDontReturnUntilYouBringMeALotOfMoneyTask task = new GoAndDontReturnUntilYouBringMeALotOfMoneyTask(parameters); 
taskExecutor.BeginTask(task); 

Cancellare/Interrompere i pulsanti vengono cablati in modo da dicono il compito di esecutore/compito di abortire.

In realtà è un po 'più complesso di quello presentato qui, ma questa è l'idea generale.

+0

Passerò in lambda per Execute, AfterSuccess, AfterFailure, AfterAbortion ma a parte questo, buon Consiglio. –

+0

Sì, puoi farlo con lambda, ma sarebbe utile solo per compiti semplici. Una logica di attività più complessa merita la propria classe. Inoltre, non è necessario implementare tutti i metodi "Dopo", quindi sarebbe meglio implementare una classe astratta di base con metodi virtuali che è possibile sovrascrivere se necessario. –