2016-06-02 6 views
0

Lesen Ich habe die folgende MethodeHowto die UI Benachrichtigen während Datensätze mit einem SqlDataReader

public List<VatRate> GetAll(string cnString) 
    { 
     List<VatRate> result = new List<VatRate>(); 
     using (SqlConnection cn = new SqlConnection(cnString)) 
     { 
      SqlCommand cmd = new SqlCommand(); 
      cmd.Connection = cn; 
      cmd.CommandText = SQL_SELECT; 
      cmd.CommandType = System.Data.CommandType.Text; 
      cn.Open(); 
      SqlDataReader reader = cmd.ExecuteReader(); 
      if (reader.HasRows) 
      { 
       while (reader.Read()) 
       { 
        VatRate vr = new VatRate(); 
        vr.IDVatRate = reader["IDVatRate"] == System.DBNull.Value ? Guid.Empty : (Guid)reader["IDVatRate"]; 
        vr.Percent = reader["Percent"].XxNullDecimal(); 
        vr.Type = reader["Type"].XxNullString(); 
        vr.VatRateDescription = reader["VatRateDescription"].XxNullString(); 
       } 
      } 
      reader.Close(); 
      cn.Close(); 
     } 
     return result; 
    } 

Es in einer WPF-Anwendung verwendet werden, und ich möchte in der Lage, die Benutzeroberfläche des Lesefortschritt zu informieren. Muss ich ein einfaches Ereignis ansprechen? etwas wie OnProgress (new MyProgressEventHandler (recordcounter)); Ich weiß sicher, dass eine Methode wie diese die Benutzeroberfläche während der Ausführung einfriert. Gibt es etwas Besseres, das man zum Beispiel mit den asynchronen Methoden machen kann, um auf die Ausführung der Methode zu warten, aber den Benutzer darüber informieren zu können?

+0

Sie sollten in der Lage sein, die Methode "async" zu machen und dann den Anruf nur zu erwarten. – itsme86

+0

Vielleicht könnte eine Idee von Progressbar mit BackgroundWorker eine der Referenzen sein. –

+0

Die Async erwarten scheint eine nette Möglichkeit, um fortzufahren, aber es löst nicht mein Fortschritt Benachrichtigungsproblem, muss ich die Async erwarten und ein Ereignis zu verwenden die UI benachrichtigen? und gibt es in diesem Fall Probleme mit Querfäden? Vielen Dank im Voraus –

Antwort

2

können Sie eine IProgress<T> übergeben und verwenden Sie es ist Report(T) Methode. Wenn das Objekt, das die Schnittstelle unterstützt, ein Progress<T> ist, wird der Rückruf auf der Benutzeroberfläche automatisch marsalisiert, wenn das Objekt auf dem UI-Thread erstellt wurde.

//elsewhere 
public class GetAllProgress 
{ 
    public GetAllProgress(int count, int total) 
    { 
     Count = count; 
     Total = total; 
    } 

    public Count {get;} 
    public Total {get;} 
} 

public List<VatRate> GetAll(string cnString, IProgress<GetAllProgress> progress) 
{ 
    List<VatRate> result = new List<VatRate>(); 
    using (SqlConnection cn = new SqlConnection(cnString)) 
    { 
     SqlCommand cmd = new SqlCommand(); 
     cmd.Connection = cn; 
     cmd.CommandText = SQL_SELECT_COUNT; 
     //You don't need to do set CommandType to text, it is the default value. 
     cn.Open(); 

     var totalCount = (int)cmd.ExecuteScalar(); 
     progress.Report(new GetAllProgress(0, totalCount)); 

     cmd.CommandText = SQL_SELECT; 
     using(SqlDataReader reader = cmd.ExecuteReader()) 
     { 
      //reader.HasRows is unnecessary, if there are no rows reader.Read() will be false the first call 
      while (reader.Read()) 
      { 
       VatRate vr = new VatRate(); 
       vr.IDVatRate = reader["IDVatRate"] == System.DBNull.Value ? Guid.Empty : (Guid)reader["IDVatRate"]; 
       vr.Percent = reader["Percent"].XxNullDecimal(); 
       vr.Type = reader["Type"].XxNullString(); 
       vr.VatRateDescription = reader["VatRateDescription"].XxNullString(); 
       result.Add(vr); 
       progress.Report(new GetAllProgress(result.Count, TotalCount)); 
      } 
      //I put reader in a using so you don't need to close it. 
     } 
     //You don't need to do cn.Close() inside a using 
    } 
    return result; 
} 

Sie können dann GetAll auf einem Hintergrund-Thread aufrufen (nur sicher sein, new Progress<GetAllProgress>() auf dem UI-Thread zu nennen) oder neu zu schreiben, um die Funktion Asynchron zu sein.

public async Task<List<VatRate>> GetAllAsync(string cnString, IProgress<GetAllProgress> progress) 
{ 
    List<VatRate> result = new List<VatRate>(); 
    using (SqlConnection cn = new SqlConnection(cnString)) 
    { 
     SqlCommand cmd = new SqlCommand(); 
     cmd.Connection = cn; 
     cmd.CommandText = SQL_SELECT_COUNT; 

     //The .ConfigureAwait(false) makes it so it does not need to wait for the UI thread to become available to continue with the code. 
     await cn.OpenAsync().ConfigureAwait(false); 

     var totalCount = (int)await cmd.ExecuteScalarAsync().ConfigureAwait(false); 
     progress.Report(new GetAllProgress(0, totalCount)); 

     cmd.CommandText = SQL_SELECT; 
     using(SqlDataReader reader = await cmd.ExecuteReaderAsync().ConfigureAwait(false)) 
     { 
      while (await reader.ReadAsync().ConfigureAwait(false)) 
      { 
       VatRate vr = new VatRate(); 
       vr.IDVatRate = reader["IDVatRate"] == System.DBNull.Value ? Guid.Empty : (Guid)reader["IDVatRate"]; 
       vr.Percent = reader["Percent"].XxNullDecimal(); 
       vr.Type = reader["Type"].XxNullString(); 
       vr.VatRateDescription = reader["VatRateDescription"].XxNullString(); 
       result.Add(vr); 
       progress.Report(new GetAllProgress(result.Count, TotalCount)); 
      } 
     } 
    } 
    return result; 
} 
+0

Ich finde diesen Code sehr interessant, aber es hat immer noch eine starke Verbindung zwischen dem Datenanbieter und der UI, was ich tun möchte, wenn möglich, trennen Sie die Datenklasse von der Benutzeroberfläche und lassen Sie es einfach kommunizieren es ist Fortschritt. Eventuell wird ein Event verwendet, das dann von der Business-Klasse abgefangen wird, die die Datenklasse verwendet und an die Benutzeroberfläche weitergegeben hat. Ich weiß nicht, ob ich hier klar bin, aber wenn Sie ein Beispiel dafür kennen, wäre es großartig. –

0

@ Scott Chamberlain Antwort ist groß, und ich werde den Einsatz der async/await Lösung vorschlagen.

Hier, ich füge nur einige Teile für WPF hinzu.

In WPF können Sie <ProgressBar> verwenden und den Wert einfach angeben, um den Fortschritt anzuzeigen.

<Grid> 
    <ProgressBar x:Name="progressBar" HorizontalAlignment="Left" Height="22" Margin="59,240,0,0" VerticalAlignment="Top" Width="383"/> 
</Grid> 

Wenn Sie GetAllAsync aufrufen, können Sie tatsächlich Code so einfach schreiben wie folgt:

await GetAllAsync(new Progress<GetAllProgress>(progress=> { progressBar.Maximum = progress.Total; progressBar.Value = progress.Count; })); 

BTW, es ist besser, MVVM Muster zu verwenden und Ihre ADO-Logik von UI zu entkoppeln, können Sie einen Blick zu diesem article.

+0

Da Sie meinen GetAllPregress aufrufen, habe ich Ihr Beispiel geändert, um den richtigen Typ von 'Progress ' zu verwenden, um meine Funktion zu übernehmen. –

+0

Danke für den Link zu dem Artikel, ich muss es lesen und den Code herunterladen, um zu verstehen Genau das, was der Autor macht, könnte etwas in der Richtung sein, was ich tun möchte. –

Verwandte Themen