2011-01-25 13 views
7

Załóżmy, że mamy aplikację o warstwowej architekturze. W widoku używamy MVC lub MVVM. Model jest traktowany jako domena, ma znaczną część logiki biznesowej.Jak wysłać aktualizacje postępu z klasy biznes/model?

Teraz powiedzmy, że mamy w modelu metodę, która zajmuje trochę czasu. Skomplikowane obliczenia lub zabiegi, które należy wykonać dla każdego elementu obiektu, na przykład.

W interfejsie użytkownika chcemy wyświetlić pasek postępu i tekst, który wyświetli bieżący krok obliczeń (na przykład listbox z całą historią procesu).

Jak byś to zrobił? Jak wysłać z modelu informacje o postępie procesu i jak podłączyć kontroler lub ViewModel, aby zaktualizować postęp?

Odpowiedz

5

Często wdrażam to w następujący sposób. Mój proces w warstwie biznesowej, który trwa długo, podnosi zdarzenia co jakiś czas, wskazując, że trafia w określone "kamienie milowe". Ty decydujesz, jakie kamienie milowe sygnalizują zdarzenia i ile z nich. Jeśli twój czasochłonny proces jest zwykłą pętlą, możesz wybrać, na przykład, powtarzać to samo zdarzenie co 10% pozycji w pętli. Jeśli jest to proces z różnymi fazami, możesz wybrać inne zdarzenie, gdy każda faza się zakończy.

Teraz twoja warstwa prezentacji subskrybuje te zdarzenia i działa w konsekwencji, aktualizując pasek postępu, tekst lub cokolwiek innego.

Ten mechanizm jest dobry, ponieważ:

  1. Warstwa firma pozostaje niezależna od tego, co może trwać nawet w warstwie prezentacji.
  2. Jest łatwy do rozbudowy do komunikacji dwukierunkowej. Możesz łatwo zmieniać zdarzenia, aby warstwa prezentacji (lub jakikolwiek inny subskrybent) mogła zwrócić flagę anulowania, aby warstwa biznesowa wiedziała, że ​​długo trwający proces musi zostać anulowany.
  3. Przyznaje pracę synchroniczną lub asynchroniczną. Oznacza to, że można go używać do blokowania wywołań (np. Prezentacja i warstwa biznesowa współużytkują ten sam wątek) lub połączeń bez blokowania (np. Twoja warstwa biznesowa używa wątku roboczego tła). Klasa System.ComponentModel.BackgroundWorker może być użyta w tym drugim przypadku, ale nie jest zbyt dobra, jeśli chcesz podnieść wiele typów zdarzeń.

Mam nadzieję, że to pomoże.

+0

Dzięki za bardzo kompletną odpowiedź.Miałem pogląd, że wydarzenia są bardzo techniczną kwestią, która nie ma miejsca w warstwie biznesowej. Ale na końcu musi istnieć sposób na przesłanie informacji o przebiegu procesu. – Gimly

+0

@ Gimly: Nie ma za co. Sądzę, że zdarzenia są użyteczne w warstwie biznesowej do implementacji "aktywnych" obiektów; scenariusz w twoim pytaniu jest dobrym przykładem. – CesarGon

0

Będziesz musiał zbadać wzór obserwatora (http://en.wikipedia.org/wiki/Observer_pattern). To dość powszechne podejście do aplikacji desktopowych. Jest to nieco bardziej skomplikowane w sieci. Możesz także zajrzeć do komety [http://en.wikipedia.org/wiki/Comet_(programming]], aby zobaczyć, jak to się robi w sieci.

+0

myślałem o strukturze obserwatora oczywiście, ponieważ jest to dość powszechne w .NET. Ale nie sądzę, żeby to było właściwe, ponieważ oznaczałoby to posiadanie technicznych spraw w biznesie, co wydaje mi się nieco błędne. – Gimly

0

Podjęłam następujące podejście do podobnej sprawy. Ten widok zawiera działania, które mogą zająć dużo czasu i chciałbym okresowo przedstawiać postępy. Długotrwała akcja zostaje przeniesiona na inną klasę, Robotnik. Niektóre akcje użytkownika inicjują wywołanie funkcji DoSomething w aplikacji TestViewModel.

TestView.xaml

... 
<!-- Progress bar --> 
<ProgressBar Visibility="Visible" Height="10" Value="{Binding SomeValue}"/> 
... 

TestViewModel.cs rozciąga BaseViewModel, BaseViewModel właśnie implementuje INotifyPropertyChanged

... 
private void DoSomething(){ 
    Worker worker = new Worker(); 
    worker.ProgressChanged += new EventHandler<WorkerEventArgs>(OnProgressChanged); 
    worker.Start(); 
} 

private void OnProgressChanged(object sender, WorkerEventArgs args){ 
    SomeValue = args.Progress; 
} 

private const String SomeValuePropertyName = "SomeValue"; 
private double someValue; 
public double SomeValue 
{ 
    get 
    { 
     return someValue; 
    } 
    set 
    { 
     if (someValue == value) 
     { 
      return; 
     } 
     someValue = value; 
     NotifyPropertyChanged(SomeValuePropertyName); 
    } 
} 
... 

Worker.cs

... 
public event EventHandler<WorkerEventArgs> ProgressChanged; 
public void Start(){ 
    //This will take a long time. Periodically call NotifyProgress 
} 

private void NotifyProgress() 
{ 
    if (ProgressChanged != null) 
    { 
     double progress = ...; //calculate progress 
     ProgressChanged(this, new WorkerEventArgs(progress)); 
    } 
} 
... 

WorkerEventArgs.cs

public class WorkerEventArgs : EventArgs 
{ 
    public double Progress { get; private set; } 

    public WorkerEventArgs(double progress) 
    { 
     Progress = progress; 
    } 
} 
2

Polecam patrząc na klasę BackgroundWorker przewidzianego w przestrzeni nazw System.ComponentModel.

Pracownik tło udostępnia metody musisz uruchomić intensywne działanie w osobnym wątku, a otrzymywać aktualizacje statusu na jego postęp (przez ReportProgress, ProgressChanged i RunWorkerCompleted).

Ja osobiście eksperymentowałem z używaniem BackgroundWorker w środowisku sieciowym, w celu uruchamiania zaplanowanych zadań. Zdecydowałem się opublikować pracę, którą wykonałem do tej pory na codeplex. Czuję, że duch mojego kodu może być przydatny w twojej sytuacji. 'Web Scheduled Task Framework' codeplex project.

Jeśli zdecydujesz się pobrać projekt, zobaczysz, w jaki sposób używam klasy BackgroundWorker w klasie . Moja implementacja nie dołącza zdarzeń postępu do pracownika, ale byłoby to bardzo łatwe. Ponadto moja obecna implementacja koncentruje się na uruchomieniu zadania w określonym przedziale czasowym, ale modyfikowanie go w celu utworzenia kolejki przetwarzania "na żądanie" nie byłoby trudne. Mogę nawet dodać to jako funkcję, o której teraz myślę :)

Zakładając, że postępowałeś zgodnie z podejściem mojego kodu powyżej, łatwo byłoby stworzyć akcję na kontrolerze, który został zwolniony, sprawdziłby listę "zadań" (lub konkretnego zadania, które Cię interesuje) i zgłoś informacje jako pewnego rodzaju ActionResult. Skonfiguruj trochę javascript, aby odpytać akcję w określonym przedziale czasu, a będziesz miał swój postęp!

Powodzenia i daj mi znać, jeśli masz jakiekolwiek pytania dotyczące mojego kodu.

1

Na podstawie pozostałych komentarzy starasz się, aby Twoja warstwa biznesowa była tak czysta, jak to tylko możliwe.

Następnie podejście model View ViewModel może zmieścić: http://en.wikipedia.org/wiki/Model_View_ViewModel

Jak calcualtion jest zrobione, rzucasz zdarzenia, że ​​postęp został złożony.

Te zdarzenia są przechwytywane w narzędziu ViewModel, a kwota postępu jest aktualizowana.

widok jest następnie aktualizowany ze względu na wiązania z danymi między ViewModel i widok (obserwator wzorzec)