Visual Basic, VB .NET, ASP, Active X, Access, SQL Server

Conecte um Recordset customizado a um banco de dados usando XML

Alguns desenvolvedores consideram atualizações em batch e Recordsets desconectados como recursos pela metade, porque eles exigem uma conexão inicial com o banco de dados, mas apenas para obter as estruturas de tabelas. Seria ótimo se o ADO permitisse que você definisse a estrutura do banco de dados sem uma conexão inicial a ele, uma vez que esta operação consome alguns segundos.

Como esta dica demonstra, a convergência entre ADO e XML oferece uma surpreendente ( e não-óbvia) solução para este problema. O código não é muito complicado e você pode reutilizar esta técnica para realizar outras operações que são habitualmente consideradas impossíveis com Recordsets ADO. 

Se você está familiarizado com a capacidade do ADO para criação de Recordsets independentes de banco de dados - isto é, pela simples adição de itens à sua coleção Fields - você também está consciente de que este recurso tem uma séria limitação: Você não pode conectar o Recordset a um banco de dados e realizar qualquer atualização em batch. O problema é que o Recordset criado sem uma conexão ao banco de dados tem uma propriedade Source vazia e, quando você o conecta ao banco de dados, você obtém o erro "Insuficiente informação de tabela base".

O que está faltando em um Recordset independente são três atributos de campo que indicam de que banco de dados, tabela e campo o valor correspondente se deriva. Se você criar um novo objeto Field usando o método Fields.Append, você não pode especificar estas propriedades nem pode adicioná-las mais tarde porque as propriedades de campo não podem ser estendidas. 

Contudo, se você estiver trabalhando com ADO 2.1 e o Internet Explorer 5 estiver instalado na máquina, há uma técnica que permite que você resolva este problema: 1. Crie um Recordset independente, abra-o e adicione todos os campos que você sabe com certeza que existem na tabela do banco de dados alvo. Você salva o Recordset no formato XML ( que é a razão de você precisar do ADO 2.1 ); 2. Re-carregue o documento XML resultante no XML parser ( você precisa de uma referência à biblioteca MSXML para fazer isto); 3. Manipule o documento e adicione os atributos de campo faltantes; 4. Salve o documento XML modificado e o reabra como um Recordset. Deste ponto em diante, o Recordset pode ser associado a uma conexão válida e você pode usá-lo para descarregar dados no banco de dados.

Veja abaixo um procedimento preparado que obtém um Recordset ADO e o nome do BaseCatalog ( o banco de dados) e da BaseTable, e então realiza todas as manipulações do XML para você e torna o Recordset conectável. O procedimento usa o objeto Stream do ADO para evitar o passo demorado de criar um arquivo temporário, assim sendo requer o ADO 2.5:

' Requer uma referência à biblioteca ADO 2.5 ou 2.6 e à biblioteca MSXML2
' Requer IE 5


Sub LinkRsToDB(ByVal rs As ADODB.Recordset, _
    ByVal BaseCatalog As String, ByVal BaseTable As String)
    Dim DOMDoc As New DOMDocument
    Dim Root As IXMLDOMNode
    Dim Schema As IXMLDOMNode
    Dim Node As IXMLDOMNode
    Dim Item As IXMLDOMNode
    Dim NewItem As IXMLDOMAttribute
   
    ' abre o Recordset se necessário
    If (rs.State And adStateOpen) = 0 Then rs.Open
   
    ' salva o Recordset diretamente no XML parser
    rs.Save DOMDoc, adPersistXML

    ' o Schema é o primeiro nó abaixo do raiz
    Set Root = DOMDoc.childNodes.Item(0)
    Set Schema = Root.childNodes(0)

    For Each Node In Schema.childNodes(0).childNodes
        If UCase(Node.baseName) = "ATTRIBUTETYPE" Then
            For Each Item In Node.Attributes
                If Item.baseName = "write" Then
                    ' Remove este atributo, que não é suportado
                    'quando o Recordset for carregado
                    Node.Attributes.removeQualifiedItem _
                        Item.baseName, Item.namespaceURI
                    Exit For
                End If
            Next

            ' Cria atributo faltante para o Recordset
            Set NewItem = DOMDoc.createAttribute("rs:basecatalog")
            NewItem.Value = BaseCatalog
            Node.Attributes.setNamedItem NewItem

            Set NewItem = DOMDoc.createAttribute("rs:basetable")
            NewItem.Text = BaseTable
            Node.Attributes.setNamedItem NewItem

            Set NewItem = DOMDoc.createAttribute("rs:basecolumn")
            ' Assume que o nome lógico é igual ao nome físico
            NewItem.Text = Node.Attributes(0).Text
            Node.Attributes.setNamedItem NewItem

            ' este atributo é exigido sob o ADO 2.5
            Set NewItem = DOMDoc.createAttribute("rs:writeunknown")
            NewItem.Text = "true"
            Node.Attributes.setNamedItem NewItem
        End If
    Next

    ' re-carrega o Recordset a partir do parser
    rs.Close
    rs.Open DOMDoc
End Sub

Eis aqui um exemplo de como você pode usar esta técnica. Primeiro, você precisa criar um Recordset independente, cuja estrutura de campos confira com a estrutura da tabela do banco de dados que você quer atualizar e adicionar um ou mais registros a ela

Dim rs As New ADODB.Recordset
Dim cn As New ADODB.Connection
   
rs.Fields.Append "au_id", adVarChar, 11
rs.Fields.Append "au_lname", adVarChar, 40
rs.Fields.Append "au_fname", adVarChar, 20
rs.Fields.Append "phone", adChar, 12
rs.Fields.Append "address", adVarChar, 40
rs.Fields.Append "city", adVarChar, 20
rs.Fields.Append "state", adChar, 2
rs.Fields.Append "zip", adChar, 5
rs.Fields.Append "contract", adBoolean

' Adiciona um novo registro
rs.Open
rs.AddNew
rs("au_id") = "978-43-6543"
rs("au_fname") = "Francesco"
rs("au_lname") = "Balena"
rs("city") = "Menlo Park"
rs("State") = "CA"
rs("Zip") = "94025"
rs("Contract") = 1
rs.Update

Agora, você pode chamar a rotina LinkRsToDB para tornar o Recordset conectável, então você abre a conexão real e conclui as operações de inclusão.

'Usa a rotina LinkRsToDB para modificar o Recordset
'e torná-lo conectável ao banco de dados:
LinkRsToDB rs, BaseCatalog:="Pubs", BaseTable:="Authors"

' abre a conexão e a associa ao Recordset
cn.Open "DSN=Pubs"
Set rs.ActiveConnection = cn

' Atualiza o banco de dados

'  (sem executar a rotina LinkRsToDB você receberá um erro
'   "Insufficient base table information for updating or refreshing.")
rs.UpdateBatch

Note que o código acima requer ADO 2.5 ou posterior

Nota importante: esta técnica foi testada com SQL Server 7, e pode não funcionar igualmente bem com todos os bancos de dados, provedores OLE DB, versões de ADO, tipos de campos, etc. O único modo de determinar quão bem ela funciona com sua configuração e banco de dados específico é testá-la.