Laden...

Wie entwickelt man eine MMVM Anwendung, wenn Tasks abhängig von Tasks sind?

Erstellt von JimStark vor 3 Jahren Letzter Beitrag vor 3 Jahren 669 Views
JimStark Themenstarter:in
309 Beiträge seit 2020
vor 3 Jahren
Wie entwickelt man eine MMVM Anwendung, wenn Tasks abhängig von Tasks sind?

Hi Leute,

bisher dachte ich die sauberste und einfachste Benutzung von Tasks in WPF ist z.B.:


// Beispiel Command an Button gebunden:
public async void AddCustomerExecute(){
   await AddCustomer(...);
...
}

Also dass die Oberfläche weiterläuft und auf die Beendigung wartet. Ich verstehe nur nicht ganz wie man die Anwendung am besten entwirft wenn Tasks abhängig von Tasks sind.

Beispiel:


public async void AddCustomerExecute(){
   await AddCustomeViewModel(...);
...
}

public Task AddCustomerViewModel(...){
// in dieser Funktion werden mehrere Tasks ausgeführt.
// async ohne Task sollte es so aussehen:
var result = await GetResults(...);
...
await AddCustomerToDb(...);
}

// Diese Funktion ist vorgegeben:
public Task AddCustomerToDb(...){
}

Wie gestalte ich nun die AddCustomerViewModel() Funktion, da ich ja z.B. in einem Task.Run( () => {...}) kein await nutzen kann? Mit Wait() und Result,.. ?

Ich hoffe man versteht was ich meine 😁 Und wo ist eigentlich Abt?

P
441 Beiträge seit 2014
vor 3 Jahren

eine async Methoden ohne Rückgabewerte (void) können zu Race conditions führen. Nutze besser async Methoden, die einen Task zurückgeben.

Du kannst auch in einer inline Funktion await nutzen, in dem du diese als async markierst.


var func = async () => {
  await ...;
  await ...;
}

await func;

Je nachdem, was du genau machen möchtest gibt es unterschiedliche Herangehensweisen an das Problem. Angefangen von kompletten Frameworks, die dir die Arbeit abnehmen über Mediation (CQRS via MediatR) bis hin zu simplem ausprogrammieren der Abhängigkeiten.
Was passt hängt davon ab, wie deine Anforderungen aussehen.

Dein Beispielcode sieht aber nach Schichtenverletzung aus. Datenbankzugriff sollte nicht in der Datenbank erfolgen.

P.S.: ist mir erst am Ende aufgefallen... willst du nur mehrere await's in einer Methode ausführen? Das funktioniert doch Problemlos..

Pseudo Code:


public async Task AddCustomer() 
{
  var customer = await AddCustomerToDb();
  await AddCustomerAddress(customer, address);
  await AddCustomerToProject(customer, 1);
}

JimStark Themenstarter:in
309 Beiträge seit 2020
vor 3 Jahren

Super, vielen Dank! Genau was ich gesucht habe.

Ich verstehe da aber den Syntax nicht ganz oder wie das genau funktioniert:



public async Task AddCustomer()
{
  var customer = await AddCustomerToDb(); //Task<Customer>
  await AddCustomerAddress(customer, address); // Task 
  await AddCustomerToProject(customer, 1); // Task
}

//vs.

public async Task AddCustomer()
{
   return Task.Run( () => {...}; // Task zurück
}



Den zweiten Fall verstehe ich, da gebe ich einen Task zurück. Wenn ich die MS Docs richtig verstanden habe ist "await xyz()" auch vom Typ Task. Wie funktioniert das intern das da was returned wird, auch wenns mehrere Tasks gibt bzw wenn ein übergeordneter Task draus gemacht wird...?

P
441 Beiträge seit 2014
vor 3 Jahren

Intern erstellt der Compiler aus jedem await eine Statemachine.
Da gibt es sicher einige Blog Posts oder Artikel darüber, wie das genau funktioniert.

Für dich erscheint es aber so, dass das warten auf eine async Methode (await) dazu führt, dass diese im Hintergrund ausgeführt wird und der Verarbeitungsschritt dann an den Rückgabepunkt zurückspringt.
Insofern ist es wunderbar möglich await's untereinander zu schreiben.

In diesem Beispiel, bei AddCustomerToDb: Wenn der Task "AddCustomerToDb" abgearbeitet ist, springt die Ausführung wieder zu dieser Zeile und die variable customer steht zur verfügung. Danach wird "AddCustomerAddress" abgearbeitet.
Also für den Programmierer kein Unterschied zu sequentieller Programmierung.


public async Task AddCustomer()
{
  var customer = await AddCustomerToDb(); //Task<Customer> 
  await AddCustomerAddress(customer, address); // Task
  await AddCustomerToProject(customer, 1); // Task
}

Auch funktionieren würde z.B. ein .ContinueWith().

Das Task.Run wirst du dir vermutlich dort sparen können, wenn die aufgerufene Methode sowieso schon einen Task zurückgibt.

JimStark Themenstarter:in
309 Beiträge seit 2020
vor 3 Jahren

Für dich erscheint es aber so, dass das warten auf eine async Methode (await) dazu führt, dass diese im Hintergrund ausgeführt wird und der Verarbeitungsschritt dann an den Rückgabepunkt zurückspringt.
Insofern ist es wunderbar möglich await's untereinander zu schreiben.

In diesem Beispiel, bei AddCustomerToDb: Wenn der Task "AddCustomerToDb" abgearbeitet ist, springt die Ausführung wieder zu dieser Zeile und die variable customer steht zur verfügung. Danach wird "AddCustomerAddress" abgearbeitet.
Also für den Programmierer kein Unterschied zu sequentieller Programmierung.


public async Task Test(){
   var customer = await GetCustomer(...);
   await AddCustomer(customer); // führt zu CustomerHasChanged = true
   await AndereFunktion(...);
   model.Name = "...";
   syncMethode(...);
   CustomerHasChanged = false;
}

Angenommen ich habe eine Methode wie in dem Beispiel. Verstehe ich das richtig dass nicht garantiert wird dass die Methoden in dieser Reihenfolger ausgeführt werden wenn keine Abhängigkeiten bestehen? Also in meiner Anwedung habe ich das Problem, ich arbeite mehrere Tasks mit await ab. Danach setzte ich CustomerHasChanged = false. Der ist dann aber true, was irgendwie bedeutet dass die Tasks noch laufen (führen zu CustomerHasChanged=true), das setzen aber schon abgearbeitet ist.

87 Beiträge seit 2016
vor 3 Jahren

Hallo,

die Methoden werden in dieser Reihenfolge ausgeführt. Sofern diese async/await richtig implementieren.

glandorf

F
10.010 Beiträge seit 2004
vor 3 Jahren

Nein, await wartet auf die Rückkehr des Tasks.

Wenn da irgendetwas parallel ausgeführt wird, hast du irgendwo ein await vergessen.

D
261 Beiträge seit 2015
vor 3 Jahren

Angenommen ich habe eine Methode wie in dem Beispiel. Verstehe ich das richtig dass nicht garantiert wird dass die Methoden in dieser Reihenfolger ausgeführt werden wenn keine Abhängigkeiten bestehen? Also in meiner Anwedung habe ich das Problem, ich arbeite mehrere Tasks mit await ab. Danach setzte ich CustomerHasChanged = false. Der ist dann aber true, was irgendwie bedeutet dass die Tasks noch laufen (führen zu CustomerHasChanged=true), das setzen aber schon abgearbeitet ist.

Das würde nur zutreffen, wenn du die Async Methoden aufrufst, aber nicht darauf wartest (await).

2.079 Beiträge seit 2012
vor 3 Jahren

Das würde nur zutreffen, wenn du die Async Methoden aufrufst, aber nicht darauf wartest (await).

Dieser Fall kann auch ungewollt auftreten, z.B. bei Events kann man keinen Task zurück geben und Commands funktionieren nur synchron.
Daher gehört für mich immer ein Handling für einen laufenden Task dazu, z.B. könnte man eine abstrakte Command-Basisklasse schreiben, die einen aktuell laufenden Task beobachtet und solange keine weitere Ausführung erlaubt.

Z.B. gibt's dort ein Beispiel, wo so ein AsyncCommand aussehen könnte:
https://johnthiriet.com/mvvm-going-async-with-async-command/