VB.NET - Preenchendo um DataSet de forma Assíncrona


Operações com banco de dados podem demorar muito tempo e isto pode se tornar um inconveniente afetando a  interface com o seu cliente. Imagine que você esta processando uma consulta que retorna um grande volume de dados e que isto esta consumindo muito tempo. Sua interface pode prever isto e emitir um aviso do tipo : Aguarde , em processamento... . Isto é na verdade um paliativo pois o cliente vai ter que ficar esperando a operação acabar e liberar o processamento do restante do programa. Qual a solução ?

Quando você faz uma chamada síncrona , a Thread que chamou é bloqueada até que o processamento da Thread chamada se complete. Uma solução para o problema acima e realizar uma chamada assíncrona que retorna imediatamente liberando a Thread que fez a chamada para continuar o processamento. Na verdade fazemos isto criando uma nova thread que irá cuidar do processamento da consulta ao banco de dados e a thread que chamou será liberada para fazer outras operações.

Uma nova instância de uma Thread é iniciada usando o construtor que toma um argumento ThreadStart delegate o qual referencia o método a ser executado quando a Thread iniciar a sua execução (Start).

O método start() de uma Thread altera o estado de Thread para ThreadState.Running permitindo ao sistema operacional agendá-lo para execução.

O formulários e os controles Windows  como o DataGrid não podem ser chamados em nenhuma Thread a não ser aquele que criou o formulário ou os controles pois eles são baseados no modelo de Threads simples.(single Thread)

Chamada de métodos de outras Threads precisam ser marshaled para a Thread de criação. Isto pode ser feito assincronamente invocando o método BeginInvoke do formulário para forçar o método a ser executado na Thread que criou o formulário ou o controle.

Apenas para você lembrar abaixo estou relacionando os principais métodos/propriedades da classe Thread:

A classe Thread esta no namespace : System.Threading

Nota: Para saber mais sobre Threads leia os artigos :

Neste artigo vou criar uma aplicação que acessa um banco de dados northwind do SQL Server e acessa as tabelas Orders e Orders Details através de um relacionamento e exibe o resultado em um DataGrid.

A diferença aqui é que irei criar um Thread para processar a consulta liberando a thread principal para continuar o processamento.

Abaixo temos a estrutura das duas tabelas usadas no artigo.

Tabela Orders Tabela Orders Details

Inicie um novo projeto no VS.NET do tipo Windows Application usando a linguagem VB.NET e no formulário padrão form1.vb inclua os seguintes componentes : Datagrid , TextBox (multiline) e Button., conforme figura abaixo:

Neste projeto vamos usar os seguintes imports

Imports System.Threading
Imports System.Runtime.Remoting.Messaging
Imports System.Data
Imports System.Data.SqlClient

 

As variáveis usadas pelo programa são:

 


    
    Delegate Sub VinculaDataSet_DataGrid_Delegate(ByVal ds As DataSet)
    ' nome das tabelas
    Private TABELA_ORDERS As [String] = "Orders"
    Private TABELA_ORDERSDETAILS As [String] = "OrderDetails"
    ' nome do relacionamento
    Private RELACAO_ORDERS_ORDERSDETAILS As [String] = "Orders_OrderDetails_Relation"
    ' nome dos campos
    Private CAMPO_ORDERID As [String] = "OrderID"
    Private CAMPO_ORDERDATE As [String] = "OrderDate"
    ' variavel que define se a tarefa foi completada
    Private ok As Boolean = False
    'string da conexão com o SQL Server
    Private conexaoSQLServer As String = "Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=Northwind"

 

 

No evento Click do botão de comando temos o código que inicia uma nova Thread e chama o método DataSetAssincrono. O código verifica se existe uma thread em execução em background carregando o DataSet antes de criar a nova Thread.

 

Private Sub btnProcessa_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnProcessa.Click
        ' verifica se o DataSet esta sendo preenchido
        If Not ok Then
            ok = True
            ' limpa o datagrid
            dgResultado.DataSource = Nothing
            ' cria e inicia uma nova thread para preencher o datagrid
            Dim thread As New Thread(AddressOf DataSetAssincrono)
             thread.Start()
        Else
            ' O DataSet ja esta sendo preenchido - exibe mensagem
            txtStatus.Text = txtStatus.Text & "O DataSet ainda esta sendo preenchido..." & Environment.NewLine
        End If
    End Sub

 

O método DataSetAssincrono carrega o DataSet com as tabelas Orders e Orders Details do banco de dados Northwind. O método VinculaDataSet é chamado assincronamente na thread do formulário para exibir os resultados.

a

Private Sub DataSetAssincrono()
        'exibe mensagem de progresson na caixa de texto
        txtStatus.Text = "Preenchendo o DataSet ..." & Environment.NewLine

        'define um dataset e um dataadapter
        Dim ds As New DataSet("Source")
        Dim da As SqlDataAdapter

        ' preenche a tabela Order e inclui no  DataSet
        da = New SqlDataAdapter("SELECT * FROM Orders", conexaoSQLServer)
        'define um objeto datatable
        Dim orderTable As New DataTable(TABELA_ORDERS)
        da.FillSchema(orderTable, SchemaType.Source)
        da.Fill(orderTable)
        ds.Tables.Add(orderTable)
        ' preenche a tabela Order Details e inclui no  DataSet
        da = New SqlDataAdapter("SELECT * FROM [Order Details]", conexaoSQLServer)
        'define um objeto datatable
        Dim orderDetailTable As New DataTable(TABELA_ORDERSDETAILS)
        da.FillSchema(orderDetailTable, SchemaType.Source)
        da.Fill(orderDetailTable)
        ds.Tables.Add(orderDetailTable)

        ' cria o relacionamento entre as tabelas
        ds.Relations.Add(RELACAO_ORDERS_ORDERSDETAILS, ds.Tables(TABELA_ORDERS).Columns(CAMPO_ORDERID), _ 
ds.Tables(TABELA_ORDERSDETAILS).Columns(CAMPO_ORDERID), True)

        txtStatus.Text = txtStatus.Text & "Preenchimento do DataSet esta completo." & Environment.NewLine
        ' chama o método VinculaDataSet_DataGrid assincronamente na thread do formulário
        Me.BeginInvoke(New VinculaDataSet_DataGrid_Delegate(AddressOf VinculaDataSet_DataGrid), New Object() {ds})
        ' define a flag indicando que o preenchimento assincrono esta completo
        ok = False
    End Sub

 

O código abaixo apenas exibe o resultado no DataGrid.

 

Private Sub VinculaDataSet_DataGrid(ByVal ds As DataSet)
        ' vincula a view padrão da tabela Orders ao datagarid
        dgResultado.DataSource = ds.Tables(TABELA_ORDERS).DefaultView
End Sub

 

Executando o projeto temos o resultado abaixo:

 

O código esta comentado e os artigos anteriores sobre Thread fornecem a teoria básica para você entender esta aplicação prática de utilização de Threads.

As Threads , quando bem usadas , fazem verdadeiros milagres; mas , não exagere no uso de Threads , pois tudo tem o seu preço...

Hasta la vista...

Eu sei , é apenas ASP.NET , mas eu gosto ...


José Carlos Macoratti