[C# + ADO.NET 2.0] SQL Query á lá "Query Analyzer"

Pagina: 1
Acties:

  • OKA
  • Registratie: Juni 2005
  • Laatst online: 03:29
Ik ben bezig met een windows-applicatie dat een sql query op een database uitvoert, zoiets als "Query Analyzer". Ik heb ontdekt dat als een query wat langer duurt dan een paar seconden, de applicatie blokkeert. Dit werd ook bevestigd door google.

Mijn vele zoekacties op google gaven oplossingen met Threads of Backgroundworker. Ook werd de "Asynchronous Processing" (geïntroduceerd in ADO.NET 2.0) als oplossing aangeboden. Maar ook werd het juist afgeraden als het niet noodzakelijk is om meerdere query gelijktijdig uit te voeren.

Wat is nu de juiste manier om een sql query uit te voeren, zonder de applicatie te blokkeren?

Ik heb al wat pogingen gewaagd met Backgroundworker. Echter liep ik al tegen problemen aan dat ik de infomessage niet meer op de GUI kon laten zien. Ik gebruik de volgende code:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
private void Button_Click(object sender, EventArgs e)
        {
            this.Cursor = Cursors.WaitCursor;
            // "ConnectionString()" bevat uiteraard de juiste ConnectionString, "this.QueryBox.Text" bevat de Query
            backgroundWorker1.RunWorkerAsync(new object[] { ConnectionString(), this.QueryBox.Text });
        }

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            object[] args = e.Argument as object[];
            string connection = args[0].ToString();
            string query = args[1].ToString();
            SqlConnection conn = new SqlConnection(connection);
            SqlCommand comm = new SqlCommand(query, conn);

            try
            {
                conn.Open();
                comm.CommandType = CommandType.Text;
                conn.InfoMessage += new SqlInfoMessageEventHandler(Query_InfoMessage);
                conn.FireInfoMessageEventOnUserErrors = true;
                int result = comm.ExecuteNonQuery();
            }
            catch (SqlException ex)
            {
                MessageBox.Show(ex.Message);
            }
            finally
            {
                conn.Close();
            }
        }

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.Cursor = Cursors.Default;
        }

void Query_InfoMessage(object sender, SqlInfoMessageEventArgs e)
        {
            for (int i = 0; i < e.Errors.Count; i++)
            {
                this.textBox1.AppendText(e.Errors[i].Message);
                this.textBox1.AppendText(Environment.NewLine);
            }
        }


Als ik met deze code via VS2005 start, krijg ik een InvalidOperationException zodra er een Infomessage getoond moet worden, met de volgende beschrijving:
Het is niet toegestaan een bewerking uit te voeren via verschillende threads: er werd vanaf een andere thread toegang gekregen tot het besturingselement textBox1 dan de thread waarop het element is gemaakt.
Als ik nu buiten VS2005, dus rechtstreeks de exe opstart en hetzelfde doe, gaat alles goed, query wordt uitgevoerd, Infomessage wordt getoond.

Doe ik nu iets verkeerd? Hoort het zo?
Is er een (andere) manier om de Infomessage (en eventueel StateChange, StatementCompleted, SqlException) i.s.m. de Backgroundworker naar de GUI-thread te krijgen?




Hoe kan ik netzoals in "Query Analyzer":
- een grid (of text) met resultaat tonen als er query-resultaat is?
- alleen berichten (o.a. Infomessage) tonen als er geen resultaat is (bv. bij een create van een storedprocedure)?

  • Phenomenon
  • Registratie: December 2000
  • Laatst online: 01-12 12:24
Ik ben er toevallig ook de laatste dagen mee bezig geweest. Waar je eens om moet zoeken is Invoke en Invokerequired.

Wat je doet is, Je spreekt de GUI aan die door een andere Thread gemaakt is. Doordat je de GUI weer wilt benaderen vanuit je background thread, wordt dit als unsafe gezien. In de exe loopt die dus blijkbaar daar wel overheen, maar het blijft unsafe om zo te werken.

Ik heb het opgelost met een Delegate en Invoke.

  • OKA
  • Registratie: Juni 2005
  • Laatst online: 03:29
Phenomenon schreef op woensdag 30 mei 2007 @ 21:22:
Ik heb het opgelost met een Delegate en Invoke.
Heb je er toevallig een voorbeeld van, of kun je mij de juiste richting sturen?

  • whoami
  • Registratie: December 2000
  • Laatst online: 00:54
Als je de backgroundworker goed gebruikt, zou je dat zelf niet meer moeten doen, aangezien die class geintroduceerd is om die problematiek te abstraheren.
(De completed en 'progress' event-handlers worden op de UI thread geinvoked).


Echter, jij krijgt die exceptie op het moment dat de InfoMessage event geraised wordt, maar ik denk niet dat je controle hebt over 'hoe' die event-handler moet geinvoked worden.

https://fgheysels.github.io/


  • DoDo
  • Registratie: Juli 2001
  • Laatst online: 21:38
Hier is een voorbeeld van delegates en invoke
C#:
1
2
3
4
5
6
7
8
9
10
delegate void addLogDelegate(string strText);
public void addLogText(string strText)
{
    if (this.InvokeRequired)
    {
        this.Invoke(new addLogDelegate(addLogText), strText);
        return;
    }
    textbox1.text = strText;
}       


Hierbij kijkt hij of hij moet invoken, zoja dan doet hij dat en veranderd hij daarna de tekst.

  • OKA
  • Registratie: Juni 2005
  • Laatst online: 03:29
DoDo schreef op woensdag 30 mei 2007 @ 22:40:
Hier is een voorbeeld van delegates en invoke
Daarmee lukt het inderdaad, thx:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void Query_InfoMessage(object sender, SqlInfoMessageEventArgs e)
{
    for (int i = 0; i < e.Errors.Count; i++)
    {
        InfoMessageText(e.Errors[i].Message);
    }
}
delegate void InfoMessageDelegate(string strText);
public void InfoMessageText(string strText)
{
    if (this.InvokeRequired)
    {
        this.Invoke(new InfoMessageDelegate(InfoMessageText), strText);
        return;
    }
        this.textBox1.AppendText(strText);
        this.textBox1.AppendText(Environment.NewLine);
}


En weet iemand hoe ik het onderstaande kan doen?
OKA schreef op woensdag 30 mei 2007 @ 21:13:
Hoe kan ik netzoals in "Query Analyzer":
- een grid (of text) met resultaat tonen als er query-resultaat is?
- alleen berichten (o.a. Infomessage) tonen als er geen resultaat is (bv. bij een create van een storedprocedure)?

  • ben15243
  • Registratie: September 2003
  • Laatst online: 30-11 12:11
Of met anonymous delegates (scheelt definitie van de delegate):

C#:
1
2
3
4
5
6
7
8
9
10
11
public void InfoMessageText(string strText) 
{ 
    if (this.InvokeRequired) 
    { 
        this.Invoke((MethodInvoker)delegate { InfoMessageText(strText); });
        return; 
    }

    this.textBox1.AppendText(strText); 
    this.textBox1.AppendText(Environment.NewLine); 
}
Pagina: 1