2017-07-24 6 views
3

Ich habe meine Anwendung auf einen minimalen POC reduziert und ich bekomme immer noch den gleichen Effekt. Es scheint, dass sich ExecuteScalarAsync wie ein synchroner Aufruf verhält. Ich dachte, dass der Rest des Codes in der asynchronen Methode pausiert wird, wenn die Wartezeit erwartet wird, und die Nachrichtenpumpe geht zurück und ruft eine weitere Nachricht aus der Nachrichtenwarteschlange ab, wodurch die UI fortfahren kann. Wenn der Scalar-Aufruf abgeschlossen ist, wird der Rest der asynchronen Methode in die Nachrichtenwarteschlange zurückgesetzt, sodass sie abgeschlossen wird.SqlClient.SqlCommand.ExecuteScalarAsync verhält sich wie synchroner Aufruf

Wenn diese kleine Anwendung ausgeführt wird, hängt die TestConnectionAsync-Methode die Benutzeroberfläche und keine anderen Nachrichten werden ausgeführt, bis der ExecuteScalarAsync-Aufruf abläuft.

Mache ich etwas falsch, oder verhält sich diese asynchrone Methode wie eine synchrone Methode?

Das Formular hat zwei Schaltflächen. Der erste führt die async-Methode aus und der zweite versucht, die async-Methode mit einem Token abzubrechen. Ich habe nie die Möglichkeit, auf den zweiten Knopf zu klicken.

Form1.cs

public partial class Form1 : Form 
{ 
    private DB _db = new DB(); 
    private string _nl = Environment.NewLine; 

    public Form1() 
    { 
     InitializeComponent(); 
    } 

    private async void button1_Click(object sender, EventArgs e) 
    { 
     textBox1.Text = "Starting" + _nl; 
     string resultString 
      = (string) await _db.TestConnectionAsync(); 
     textBox1.AppendText(resultString + _nl); 
     textBox1.AppendText("Done" + _nl); 
    } 

    private void button2_Click(object sender, EventArgs e) 
    { 
     textBox1.AppendText("Cancelling..." + _nl); 
     _db.CancelTest(); 
     textBox1.AppendText("Submitted Cancel Token" + _nl); 
    } 
} 

DB.cs

public class DB 
{ 
    private SqlCommand _command = null; 
    private CancellationTokenSource _tokenSource 
     = new CancellationTokenSource(); 
    private CancellationToken _token; 

    public async Task<string> TestConnectionAsync() 
    { 
     _token = _tokenSource.Token; 
     string query = "SELECT COUNT(*) FROM tblDintData"; 

     try 
     { 
      using (SqlConnection connection 
       = new SqlConnection(BuildConnectionString())) 
      { 
       connection.Open(); 
       _command = new SqlCommand(query, connection); 
       await _command.ExecuteScalarAsync(_token); 

       return "Successful Database Connection"; 
      } 
     } 
     catch (Exception ex) 
     { 
      return "Connection Failed:" 
       + Environment.NewLine + ex.Message; 
     } 
    } 

    public void CancelTest() 
    { 
     _tokenSource.Cancel(); 
    } 

    private string BuildConnectionString() 
    { 
     string ret = ""; 

      ret = "Server=NotARealServer;" 
       + "Database=NoSuchDatabase;" 
       + "Trusted_Connection=True;"; 

     return ret; 
    } 
} 

EDIT ***

Ok, entdeckte ich etwas nur durch Versuch und Irrtum. Wenn ich Connection.Open asynchron mache, indem ich stattdessen Connection.OpenAsync aufruft, reagiert die Benutzeroberfläche plötzlich. Dies ist nicht intuitiv, aber das ist die Linie, die ich geändert:

aus:

   connection.Open(); 

zu:

   await connection.OpenAsync(); 

Allerdings ist die ExecuteScalarAsync noch nicht annullieren, wenn ich die CancellationTokenSource abzubrechen. Irgendwelche Ideen???

+0

Dies könnte Ihnen helfen, in die richtige Richtung zu gehen https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/asynchronous-programming – Hassan

+0

Vielen Dank Hassan. Ich habe mir diese Seite angesehen, aber sie scheint meine grundlegende Frage nicht zu beantworten. Ist ExecuteScalarAsync wirklich asynchron? – dtaylor

+0

Tatsächlich ist es asynchron. Der oben erwähnte Link soll Ihnen dabei helfen, Async-Methoden aufzurufen, damit sie asynchron bleiben. Wie für die Methode selbst, https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand.executescalarync?view=netframework-4.7 – Hassan

Antwort

4

ExecuteScalarAsync ist in der Tat eine asynchrone Methode, aber Ihre UI hält an, weil Sie die Methoden nicht asynchron aufrufen. Sie können sehen, wie Sie asynchrone Methodenaufrufe unter this Microsoft page besser verarbeiten können.

Sie müssen die Verbindung auch asynchron öffnen, wie Sie auch herausgefunden haben. Der Link enthält gute Beispiele für eine offene Verbindung, das Abrufen von Daten und das asynchrone Abbrechen der Abfrage.

EDIT ** von Doug

Ja, Hassan richtig. Ich hatte aufgelegt, dass ExecuteScalarAsync funktioniert, wenn das ganze Problem bei Open lag. Als Faustregel in der Zukunft werde ich immer Befehle wie folgt aufrufen:

   await connection.OpenAsync(_token); 
       _command = new SqlCommand(query, connection); 
       await _command.ExecuteScalarAsync(_token); 

Auf diese Weise, wenn es ein Verbindungsproblem ist, wird die asynchrone und abbrechen Verhalten immer noch funktionieren.

Vielen Dank Hassan.

Verwandte Themen