DB Pooling

Com a versão 2.0 do .NET Framework, novos métodos foram criados para o gerencimento do pool de conexões para Databases, que até então, não tínhamos acesso. De toda a forma, a pooling ainda é gerido pelo .NET, mas temos agora métodos para poder limpar este pool de conexões.

Esta funcionalidade está disponível para os Providers SqlClient e OracleClient, em dois métodos estáticos:

               * ClearPool(Connection) – Limpa o pool e fecha as conexões ativas para uma conexão específica.
                              [ SqlClient ] [ OracleClient ]

               * ClearAllPools – Limpa o pool e fecha todas as conexões ativas.
                              [ SqlClient ] [ OracleClient ]

Contentores de Dados em ASP.NET

Bem, qual a melhor forma de transportar dados em uma aplicação Web? Se estivesse trabalhando em uma aplicação Windows Forms, acho interessante o uso do Dataset, visto que muitas vezes a conexão com a internet ou mesmo com o servidor de DB não é contínua, e sendo assim, não é possível a cada dado que precisarmos irmos até este servidor, buscar o que necessitamos. É neste cenário que olho com bons olhos para o Dataset, onde recuperamos os dados de uma DB qualquer, armazenamos neste Dataset, fechando a conexão com o servidor de DB e trabalhamos com esse Dataset, desconectado!

Uma vez que finalizamos o trabalho neste dados desconectados, reabrimos a conexão com a DB e persistimos esses na Base de Dados e pronto, tudo já estará lá atualizado.

Vale lembrar que quando estou me referindo à Windows Forms, estou querendo abranger sistemas para Windows – Desktop, ou mesmo aplicações para Pockets. Mas agora, e em uma aplicação Web? Será que vale mesmo a pena utilizar Datasets em uma aplicação ASP.NET? Na minha opinião, não acho isso lá muito interessante.

Se para acessar e trabalhar em uma aplicação ASP.NET/Web, tem que estar 100% OnLine, então vejo que não há a necessidade de utilizar o Dataset para recuperar e trabalhar com esses dados “localmente”. Já sabemos que o uso do DataReader é muitas vezes mais rápido que o Dataset (isso evita colocar aqui PPT’s que confirmem isso :P), e é uma ótima opção. Quem realmente defende Datasets, condenam os DataReaders por eles serem somente “foward-only” e “read-only”, que somente com essas características não é possível trabalhar com os dados “desconectadamente”.

Fiz todos os cursos da carreira MCSD.NET, e vi exaustivamente Datasets nestes cursos (que pelo fato de não usá-los, não me recordo), inclusive no curso de ASP.NET, ou seja, a Microsoft também “prega” o uso deles. Se o mesmo demora muito à carregar, então porque não se utiliza DataReader para recuperar e assim exibir os dados? Talvez o conceito de Datasets não está muito bem definido para o pessoal, visto que vejo códigos do tipo:

          Private Sub Page_Load(…)
               If Not Page.IsPostBack() Then
                    CarregaDS()
               End If
          End Sub

          Private Sub CarregaDS()
               MeuDataAdapter.Fill(MeuDataSet)
               MeuDataGrid.DataSource = MeuDataSet
               MeuDataGrid.DataBind()
          End Sub

          Private Sub DataGrid1_PageIndexChanged(….)
               MeuDataGrid.CurrentPageIndex = e.NewPageIndex
               CarregaDS()
          End Sub

Como podem ver neste caso, estamos usando um objeto extremamente pesado para carregar os dados da DB (isso sem contar quando o Dataset não é armazenado em Session). Para isso, ao invés de utilizar Dataset, porque não utilizam DataReader, recuperando da DB apenas os registros que são necessários para a construção e exibição da página (me refiro à paginação do DataGrid) requerida pela usuário?

Sem contar que, se criar uma aplicação realmente orientada à objetos, deve ficar terrivelmente complicado preencher e popular os objetos da aplicação, objetos quais deixam a aplicação mais próxima do mundo real. E aqui entraria o OR/M, mas deixa isso para um outro post/artigo.

Pois bem, na minha opinião, não acho o uso de Datasets viável em uma aplicação Web, ou melhor, talvez há alguma utilidade sim, como por exemplo para armazenar um retorno de Web Services, mas fora isso, acho que o DataReader muito mais ideal, mesmo sendo somente leitura.

Update:
Com relação ao CRUD na DB, claro que não utilizo o Dataset para isso, ou seja, executo os comandos diretamente na DB. Se for alguma aplicação (claro, ASP.NET) simples, utilizo uma DAL, mas se for algo mais complexo, o ideal é modelar calmamente, e assim criar aos DAOs que serão responsáveis pela persistência dos objetos na DB. Viva o OR/M 🙂

Service is running

Há aproximadamente dois meses iniciei um projeto onde tive que construir um Windows Service (ou WinService). Pois bem, a finalidade do mesmo é resgatar os registros de uma determinada Base de Dados e enviar para um orgão chamado SERASA.

Este orgão tem a finalidade de fornecer informações sobre créditos e restrições financeiras, entre outros documentos e serviços, relacionados à pessoas físicas ou jurídicas (Empresas). Eles fornecem várias formas de resgatar do cliente e devolver para o mesmo tais informações. Alguns dos meios são Sockets, HTTPs e algo que não estou recordando agora (uma solução da IBM).

Como a idéia era criar tudo em .NET, fomos logo lá entender o problema. Ao cadastrar os Clientes na Base de Dados de um sistema interno (desenvolvido em Delphi), o serviço deverá correr sem nenhuma intervenção humana (claro, é esta a finalidade de um WinService) e automaticamente gerar a String de envio e receber o valor de retorno do SERASA. Vale lembrar que muitas vezes, o retorno não é imediato, tendo assim que ficar de tempo em tempo enviando uma String para checar se o lote que foi enviado já foi ou não processado.

Para que o processo corresse corretamente, tivemos que nos atentar à algumas condições, pois existiam seis tipos de documentos a serem enviados e recebidos. O processo de resgate dos dados da Base de Dados, a geração da String de envio, o recebimento e análise do retorno e a persistência na DB são iguais para todos, ou seja, os passos a serem executados devem ser exatamente os mesmos. A solução neste caso, foi criar uma classe Base que implementa um Template Method e dentro dele define os passos do algoritmo, deixando as subclasses (as classes derivadas) implementarem os métodos como eles devem ser, ou seja, é exclusivo para cada subclasse, pois a geração da String de envio por exemplo é diferente entre os tipos de documentos a serem enviados.

Bem, depois do envio e recebimento OK, vem a parte da persistência dos dados retornados pelo SERASA na Base de Dados. Neste caso, tenho cerca de 23 tabelas com média 15 à 30 campos em cada uma delas. Como tenho os meus objetos na minha aplicação e não queria criar uma DAL/DAO para resgatar os dados e assim popular cada um dos meus objetos, e estes por sua vez adicionar à coleção adotamos o NHibernate (ferramenta de OR/M), e com isso, diminuiu à 0 (Zero) a escrita de códigos T-SQL e Stored Procedures.

Por fim, é criado as Threads que são necessários para que essas “rotinas” corram “side-by-side”, e assim aumentando a facilidade e produtividade interna da empresa que está a utilizar este WinService.

O/R Mapping – NHibernate – Resource Files

Bem, a batalha para implementar o NHibernate continua. Precisei há pouco persistir o estado dos meus objetos nas 21 tabelas da DB (SQL Server 2000). Utilizando o NHibernate, o que tive a fazer foi somente criar os arquivos de mappings (a parte mais chata, mas nada muito complicado) e também definir o arquivo de configuração do NHibernate mapeando os arquivos de resources (conforme figura abaixo), quais optei por faze-los em “Embedded Resources”.

Feito isso, é agora somente chamar o método Save(…) e passar o objeto à persistir e tudo magicamente aparece nas devidas tabelas da DB sem escrever uma query SQL, definir parametros, definir e gerir conexões, etc.

Se não estiver a utilizar “Embedded Resources” pode ao invés de fazer o mapping para o Resource, faz-se o mapping diretamente para o arquivo XML.

Bem, aos poucos vou colocando por aqui as novidades sobre este O/RM.

Lendo Arquivos TXT com DataReader

Uma forma rápida de ler arquivos textos sem utilizar as classes contidas no Namespace System.IO e manipular linha a linha do conteúdo do arquivo texto, é utilizando Odbc, que através do mesmo vocè informa na ConnectionString o path do arquivo texto. Na Query faz o SELECT apontando a cláusula FROM para a tua fonte, que no caso é o nome do arquivo.

Definido esses passos, agora pode tratar normalmente como se estivesse utilizando uma Base de Dados Relacional. Exemplo para popular os dados de um DataGrid com os dados vindos do arquivo texto que contém os seguintes dados:

[ Conteúdo do Arquivo.txt ]

Categoria, Codigo
ASP.NET, 1
VB.NET, 2
C#, 3

[ Código ]

Imports System.Data.Odbc
Dim cnn As New OdbcConnection(“Driver={Microsoft Text Driver (*.txt; *.csv)};DBQ=C:”)
Dim cmd As New OdbcCommand(“SELECT * FROM Arquivo.txt”, cnn)
Dim dr As OdbcDataReader
Try
    cnn.Open()
    dr = cmd.ExecuteReader(CommandBehavior.CloseConnection)
    With Me.DataGrid1
        .DataSource = dr
        .DataBind()
    End With
Catch ex As Exception
    Response.Write(“Ocorreu um erro.”)
Finally
    If Not (dr Is Nothing) Then dr.Close()
End Try

Reparem que a primeira linha do conteúdo do “Arquio.txt” torna-se o nome das colunas dos “registros”, qual neste caso, rotula os Headers das colunas do DataGrid. Vale chamar a atenção que o delimitador é a vírgula (“,”).

DataReader para DataSet

Ontem recebi um email de um amigo que me pedia se existia alguma forma de pegar o conteúdo de um DataReader e colocar em um DataSet.

Nunca tinha feito isso, e tinha certeza que é possível. Fiquei com tanta curiosidade que decidi colocar a “mão na massa” e tentar fazer essa funcionalidade.

Inicialmente sei que era possível percorrer os registros retornados. A única dúvida que estava um pouco preocupado, foi em como resgatar as colunas (tanto seus valores, quantos seus nomes). Pois bem, fui o Help e procurei pelos membros do DataReader e encontrei a propriedade FieldCount, qual retorna o número de colunas da linha em questão. Com isso, “matei” um problema, mas ainda faltava saber como resgatar o nome da coluna. Da mesma forma, fui ao Help e encontrei o método GetName, que dado um índice, ele retorna o nome desta coluna.

Depois disso, ficou simples, basta apenas percorrer os registros (linhas) do DataReader, e a cada linha, percorrer as colunas da mesma. O código abaixo, ilustra o processo:

        Dim conn As New SqlConnection(“ConnectionString”)
        Dim cmd As New SqlCommand(“SELECT * FROM Categories”, conn)
        Dim dr As SqlDataReader

        Dim i As Integer = 0
        Dim dRow As DataRow
        Dim num2 As Integer
        Dim ds As DataSet = New DataSet

        Try
            conn.Open()
            dr = cmd.ExecuteReader(CommandBehavior.CloseConnection)
            If dr.HasRows() Then
                ds.Tables.Add(“NomeTabela”)
                While dr.Read
                    dRow = ds.Tables(0).NewRow
                    num2 = 0
                    Do While (num2 < dr.FieldCount)
                        If (i = 0) Then
                            ds.Tables(0).Columns.Add(dr.GetName(num2))
                        End If
                        dRow(num2) = dr.GetValue(num2)
                        num2 = (num2 + 1)
                    Loop
                    i = (i + 1)
                    ds.Tables(0).Rows.Add(dRow)
                End While
                dr.Close()
            End If
        Catch ex As Exception
            MessageBox.Show(ex.ToString)
        Finally
            If Not
(dr Is Nothing) Then
                dr.Close()
            End If
        End Try

Mas uma coisa que fique bem claro: isso não quer dizer que gosto de DataSets 😉

Evento InfoMessage

Existe um Evento da SqlConnection que retorna todas as mensagens geradas pelo SQL Server que não são interpretadas como Exception. É possível recuperar tais mensagens através da SqlConnection. Para isso podemos utilizar o Evento InfoMessage que retorna isso. Abaixo um exemplo de como utilizá-lo:

Criando a StoredProcedure:

CREATE PROCEDURE InfoMessage AS
Print ‘Disparado pelo InfoMessage’
GO

No Evento Load do Formulário:

Private WithEvents conn As SqlClient.SqlConnection
Private Sub Form1_Load(…) Handles MyBase.Load
    conn = New SqlConnection(“…”)
    Dim cmd As SqlCommand = New SqlCommand(“InfoMessage”, conn)
    cmd.CommandType = CommandType.StoredProcedure
    Try
        conn.Open()
        cmd.ExecuteScalar()
        conn.Close()
    Catch ex As Exception
        MessageBox.Show(ex.ToString)
    End Try
End Sub

E no Evento InfoMessage da SqlConnection, capturamos a mensagem da StoredProcedure:

Private Sub conn_InfoMessage(ByVal sender As Object, ByVal e _
    As System.Data.SqlClient.SqlInfoMessageEventArgs) Handles conn.InfoMessage

    MessageBox.Show(e.Message)
End Sub



Com isso, a mensagem que é escrita através do Print do SQL Server, será capturado pelo Evento, e poderá fazer tratamentos no Sistema.

DataAdapter

Para os desenvolvedores que utilizam DataSets e DataAdapter, aqui vai uma dica:

Quando utilizamos o DataAdapter, não há a necessidade de abrirmos a conexão com a Base de Dados manualmente, pois ele se encarrega de fazer isso implicitamente. Basta informar a ConnectionString:

Imports System.Data.SqlClient

Dim ds As New DataSet()
Dim da As New SqlDataAdapter(“SELECT * FROM authors”, “ConnectionString”)
da.Fill(ds)
Me.DataGrid1.DataSource = ds

ConnectionString

Para quem pretende prestar os exames da Microsoft para desenvolvimento, uma das questões que é uma “pegadinha” são as possíveis formas de montar a ConnectionString.

Há várias keywords para a mesma finalidade. Mas keywords que realmente nos confudem.

Abaixo uma Lista com as principais keywords:

* Connection Timeout ou Connect Timeout
* Initial Catalog ou Database
* User ID ou UID
* Password ou Pwd
* Datasource ou Server ou Address ou Addr ou Network Address
* Integrated Security ou Trusted_Connection

Links Relacionados:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemDataSqlClientSqlConnectionClassConnectionStringTopic.asp

http://www.connectionstrings.com/