2009-05-06 5 views
7

Ich verwende das Model-View-Presenter-Muster in einem WinForms-Projekt und ein Problem (unter vielen) ist, wenn das Formular dem Moderator sagt, etwas zu tun und dann ist unreaktiv, während der Moderator es tut. Zum Glück in meinem Projekt habe ich kein Problem damit, alle Moderatoren Anrufe asynchron zu machen, die Frage ist, wie genau es geht?Best Practices für asynchrone Aufrufe in MVP mit WinForms

Sollte jeder Moderator Anruf nur in einer neuen Thread Schöpfung gewickelt werden? *

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

Was hier Best Practices ist? Was passiert, wenn der Benutzer auf die Schaltfläche "Abbrechen, was Sie tun" drückt? Wie kann ich elegant abbrechen?

. * Realistisch gesehen würde ich wahrscheinlich verwenden nur so etwas wie ein Proxy auf dem Präsentator, dies zu tun, anstatt die Thread-Erzeugung in der WinForm setzen

+0

Überrascht, hier keine echte Beteiligung zu sehen. Das hätte mich auch interessiert. – Houman

Antwort

2

Ich kann nur behaupten, dass ich darüber nachgedacht habe (vor dem Lesen Ihrer Frage;). Zuerst rüste ich die Orte aus, an denen das wirklich zählt; zum Beispiel der DB-Zugriffsdrosselpunkt. Wenn es einen Ort gibt, der nicht im "UI" -Kontext ausgeführt werden soll (Sie können ihn im UI-Thread von http://msdn.microsoft.com/en-us/library/system.threading.synchronizationcontext.current.aspx speichern und später mit dem Nicht-UI-Synchronisierungskontext vergleichen), dann debug.BitchAndMoan() darüber. Irgendwelche längeren Berechnungen (die alle in ihren eigenen Mannigfaltigkeiten klar getrennt sein sollten; richtig;) sollten das bestätigen.

Ich denke, Sie sollten mindestens die Art der Ausführung der Presenter-Funktion über Attribut konfigurierbar, die dann von Proxy befolgt wird. (nur für den Fall, dass Sie etwas seriell gemacht haben wollen).

Das Abbrechen einer Aufgabe ist eigentlich das Problem des Referenten, aber Sie müssen über ein Referenzobjekt verfügen, das angibt, was Sie stoppen möchten. Wenn Sie den Proxy-Weg gehen, könnten Sie erstellte Threads mit IAsyncResult in die Task-Liste aufnehmen, aber es ist immer noch ein Problem zu entscheiden, welcher abgebrochen werden soll, wenn die gleiche Aktion mehrmals parallel aufgerufen werden darf. Sie müssen die Aufgabe daher mit einem geeigneten anrufspezifischen Namen versehen, wenn Sie sie starten. was viel zu viel Logik in View-Seite bedeutet -> Presenter sollte wahrscheinlich View fragen, um den Benutzer zu fragen, welche der Aufgaben entsorgt werden sollte.

Meine Erfahrung ist, dass dies in der Regel nur durch die Verwendung von Ereignissen (SCSF-Stil) funktioniert. Wenn ich es von Grund auf neu mache, würde ich den Proxy-Weg gehen, da SCSF auf so viele Arten ein Schmerz war, dass ich die geistige Gesundheit seiner Designer bezweifle.

+0

Nicht zu viele Probleme mit Chokepoints in meiner Anwendung, nur 5 oder 6 verschiedene Orte, an denen eine Benutzerinteraktion an einen Moderator weitergeleitet wird, und alle können es sich leisten, asynchron zu sein. Wie wäre es, den neuen Thread tatsächlich zu drehen? neues Thema? Hintergrundarbeiter? Etwas anderes? –

+1

I _think_ BW ist mehr für eine bekannte Aufgabe gedacht, die immer "dasselbe" Ding macht, also sollte der Laich-Thread hier idiomatisch sein. Je nachdem, was Sie tun möchten, können Sie threadpool verwenden: http://msdn.microsoft.com/en-us/library/ms973903.aspx. Standardmäßig ist threadpool 25 tief, also könntest du das konfigurieren/testen (dieses Limit trifft nicht zu, wenn du einfach immer einen neuen Thread erstellst) –

0

Warum nicht den Proxy-Muster, das Sie ein paar Rückrufe verwenden machen akzeptieren die Ergebnisse zurückgeben oder abbrechen?

+0

Es tut mir leid, aber das beantwortet meine Frage in keiner Weise. Ich frage, was die besten Praktiken für das asynchrone Ausführen von Anrufen sind? Neues Thema? Hintergrundarbeiter? Wie mache ich gezielt einen Abbruch? Ich muss mir keine Gedanken über Rückrufe machen, das ist ein traditioneller MVP, so dass der Moderator nie etwas zurückgibt. –

4

ich jede Handlung in der Regel platzieren, die (realistisch) mehr als ein oder zwei Sekunden in eine separate Aufgabe übernehmen kann, so etwas wie:

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

Ich habe auch eine Abstraktion für die Ausführung solcher Aufgaben:

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

Einer der Implementierungen dieser ITaskExecutor wird mit dem 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(); 
    } 

    ... 
} 

Ich verlasse mich stark auf die Abhängigkeitsinjektion und den IoC, um Dinge miteinander zu verbinden. In dem Moderator dann nenne ich nur so etwas wie:

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

Abbrechen/abbrechen Tasten werden dann verdrahtet, so sagen sie der Aufgabe Testamentsvollstrecker/Aufgabe abzubrechen.

Es ist tatsächlich ein bisschen komplexer als hier vorgestellt, aber das ist die allgemeine Idee.

+0

Ich würde Lambdas für Execute, AfterSuccess, AfterFailure, AfterAbortion aber anders übergeben, guter Rat. –

+0

Ja, Sie können es mit Lambda tun, aber das wäre nur für einfache Aufgaben nützlich. Komplexere Aufgabenlogik verdient eine eigene Klasse. Außerdem müssen Sie nicht wirklich alle "Nachher" -Methoden implementieren, daher wäre es am besten, eine abstrakte Basisklasse mit virtuellen Methoden zu implementieren, die Sie bei Bedarf überschreiben können. –