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